├── .gitignore ├── README.md ├── TriggerBot.py ├── TriggerBotMarkov.py ├── TriggerBotSqlite.py ├── TriggerBotTornado.py ├── TriggerBot_Old.py ├── example_dot_env └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .db 7 | .idea 8 | .env 9 | venv 10 | /TriggerBotMarkov.py.db 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TriggerBot 2 | Python 2.7/3.5 Bot for Telegram. 3 | 4 | #### Setup: 5 | Run `sudo pip install -r requirements.txt` on your terminal. 6 | Then just run one of the following: 7 | 8 | ##### TriggerBot.py: 9 | -This version saves triggers for each group separately. 10 | 11 | ##### TriggerBot_old.py: 12 | -This version saves triggers globally. 13 | 14 | ##### TriggerBotTornado.py: 15 | -Same as TriggerBot.py, but runs on top of a tornado webserver. 16 | 17 | 18 | ##### TriggerBotMarkov.py: 19 | -This version does not store triggers, it stores messages and tries to generate sentences. 20 | 21 | ##### TriggerBotSqlite.py: 22 | -Same as TriggerBot.py, but uses a sqlite database to store triggers. 23 | -------------------------------------------------------------------------------- /TriggerBot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import telebot 3 | import json 4 | from time import time, asctime, sleep 5 | from os.path import exists 6 | from telebot.apihelper import ApiException 7 | # Editado 21 de junio 8 | __version__ = 0.10 9 | # comment to use default timeout. (3.5) 10 | # telebot.apihelper.CONNECT_TIMEOUT = 9999 11 | # Git Repo: 12 | # https://github.com/sanguchi/TriggerBot 13 | 14 | # GLOBAL VARIABLES. 15 | 16 | # Bot owner, replace with your user_id. 17 | owner = 59802458 18 | 19 | # Variable to hold all Triggers. 20 | triggers = {} 21 | 22 | # Separator character. 23 | separator = '/' 24 | 25 | 26 | # Check if a message is too old. 27 | def is_recent(m): 28 | return (time() - m.date) < 60 29 | 30 | # END OF GLOBAL VARIABLES SECTION. 31 | 32 | 33 | # TRIGGERS SECTION 34 | # Check if Triggers file exists and load, if not, is created. 35 | if exists('triggers.json'): 36 | with open('triggers.json') as f: 37 | triggers = json.load(f) 38 | print('Triggers file loaded.') 39 | else: 40 | with open('triggers.json', 'w') as f: 41 | json.dump({}, f) 42 | 43 | 44 | # Function to save triggers list to a file. 45 | def save_triggers(): 46 | with open('triggers.json', 'w') as f: 47 | json.dump(triggers, f, indent=2) 48 | print('Triggers file saved.') 49 | 50 | 51 | # Function to get triggers list for a group. 52 | def get_triggers(group_id): 53 | if(str(group_id) in triggers.keys()): 54 | return triggers[str(group_id)] 55 | else: 56 | return False 57 | 58 | # END OF TRIGGERS SECTION 59 | 60 | 61 | # BOT INIT SECTION. 62 | token = '' 63 | 64 | # Check if Token file exists, if not, create. 65 | if exists('token.txt'): 66 | with open('token.txt') as f: 67 | token = f.readline().strip() 68 | print('Token Loaded') 69 | else: 70 | msg = 'No token file detected, please paste or type here your token:\n> ' 71 | try: 72 | # Python 2.7 73 | token = raw_input(msg) 74 | except NameError: 75 | # Python 3.x 76 | token = input(msg) 77 | with open('token.txt', 'w') as f: 78 | f.write(token) 79 | print('Token File saved.') 80 | 81 | # Create Bot. 82 | bot = telebot.TeleBot(token) 83 | # Bot user ID. 84 | bot_id = int(token.split(':')[0]) 85 | print('Bot ID [%s]' % bot_id) 86 | 87 | 88 | # Define a custom Listener to print messages to console. 89 | # Python2 version 90 | def listener2(messages): 91 | for m in messages: 92 | cid = m.chat.id 93 | name = m.from_user.first_name.encode('ascii', 'ignore').decode('ascii') 94 | if(m.content_type == 'text'): 95 | message_text = m.text.encode('ascii', 'ignore').decode('ascii') 96 | else: 97 | message_text = m.content_type 98 | print('{}[{}]:{}'.format(name, cid, message_text)) 99 | 100 | 101 | # Python3 version. 102 | def listener3(messages): 103 | for m in messages: 104 | print('%s[%s]:%s' % (m.from_user.first_name, m.chat.id, m.text if m.text else m.content_type)) 105 | 106 | 107 | # Change to listener2 if this complains about encoding. 108 | bot.set_update_listener(listener2) 109 | 110 | # END OF BOT INITIALIZATION SECTION. 111 | 112 | # GLOBAL MESSAGES SECTION. 113 | about_message = ''' 114 | TriggerBot *%s* 115 | [Source Code on Github](https://github.com/sanguchi/TriggerBot/) 116 | [Give me 5 Stars](https://telegram.me/storebot?start=TriggerResponseBot) 117 | ''' % __version__ 118 | 119 | help_message = ''' 120 | You need help! 121 | *Commands:* 122 | `/add / ` 123 | |-_Adds a new trigger._ 124 | `/del ` 125 | |-_deletes trigger if exists._ 126 | *More:* 127 | /about - /size - /all - /source 128 | *For a detailed help send /help in private.* 129 | ''' 130 | 131 | full_help = ''' 132 | You really need help! 133 | *Main Functions:* 134 | *Add Triggers:* There are two ways. 135 | 1)`/add / ` 136 | Example: 137 | */add Hello / Hello there!* 138 | Also works via reply. 139 | _In Reply to Another Message:_ 140 | 2)`/add ` 141 | *Delete Triggers:* 142 | `/del ` 143 | Deletes a defined trigger, example: 144 | `/del hello` 145 | *Misc:* 146 | /size 147 | _Returns size of triggers list._ 148 | /all 149 | _List all triggers._ 150 | /help 151 | _This message._ 152 | /source 153 | _Sends source code TriggerBot.py_ 154 | /solve 155 | _Resolve what trigger causes the given response, if exists._ 156 | *Also works by reply:* 157 | _Reply to any bot response with the command to get the trigger._ 158 | /about 159 | _About this bot._ 160 | ''' 161 | 162 | trigger_created_message = ''' 163 | New Trigger Created: 164 | Trigger [{}] 165 | Response [{}] 166 | ''' 167 | 168 | global_trigger_created_message = ''' 169 | New Global Trigger Created: 170 | Trigger [{}] 171 | Response [{}] 172 | ''' 173 | 174 | invited_message = ''' 175 | Okay, Hi everyone, i'm *Trigger*, a bot made to store sentences as triggers. 176 | And these words will trigger a defined response. 177 | By default, hi have 4 triggers defined. 178 | Type `tutorial` to see. 179 | _Be nice._ 180 | ''' 181 | 182 | global_trigger_deleted_message = ''' 183 | Trigger [{}] deleted from {} Groups. 184 | ''' 185 | tutorial = ''' 186 | Reply to this message with '/solve' to know what word triggers this message. 187 | Reply to this message with '/del' to delete this tutorial message. 188 | Reply to this message with '/add something' to set the word something as a trigger for this message. 189 | Write /all to see all defined triggers. 190 | Write /size to see how many triggers are defined. 191 | Send a message with your chat rules, and then reply to that message with: 192 | /add #rules 193 | To save them in a trigger. 194 | ''' 195 | default_triggers = { 196 | 'trigger': 'Are you triggered?', 197 | 'oh shit': 'TRIGGERED!', 198 | 'tutorial': tutorial, 199 | 'fuck': 'Watch your language!'} 200 | 201 | # END OF GLOBAL MESSAGES SECTION. 202 | 203 | # COMMAND IMPLEMENTATION SECTION. 204 | 205 | 206 | # Adds a new trigger 207 | @bot.message_handler(commands=['add']) 208 | def add(m): 209 | # Create trigger on message reply 210 | if(m.reply_to_message): 211 | if(m.reply_to_message.text): 212 | # Reply message does not contain the trigger word/phrase. 213 | if(len(m.reply_to_message.text.split()) < 2): 214 | bot.reply_to(m, 'Bad Arguments') 215 | return 216 | trigger_word = u'' + m.text.split(' ', 1)[1].strip().lower() 217 | trigger_response = u'' + m.reply_to_message.text.strip() 218 | else: 219 | bot.reply_to(m, 'Only text triggers are supported.') 220 | return 221 | # Create trigger 222 | else: 223 | # Validations. 224 | if(len(m.text.split()) < 2): 225 | bot.reply_to(m, 'Bad Arguments') 226 | return 227 | if(m.text.find(separator, 1) == -1): 228 | bot.reply_to(m, 'Separator not found') 229 | return 230 | rest_text = m.text.split(' ', 1)[1] 231 | trigger_word = u'' + rest_text.split(separator)[0].strip().lower() 232 | trigger_response = u'' + rest_text.split(separator, 1)[1].strip() 233 | 234 | if(len(trigger_word) < 4): 235 | bot.reply_to(m, 'Trigger too short. [chars < 4]') 236 | return 237 | if(len(trigger_response) < 1): 238 | bot.reply_to(m, 'Invalid Response.') 239 | return 240 | if(len(trigger_response) > 3000): 241 | bot.reply_to(m, 'Response too long. [chars > 3000]') 242 | return 243 | # Save trigger for the group 244 | if(m.chat.type in ['group', 'supergroup']): 245 | if(get_triggers(m.chat.id)): 246 | get_triggers(m.chat.id)[trigger_word] = trigger_response 247 | else: 248 | triggers[str(m.chat.id)] = {trigger_word: trigger_response} 249 | msg = u'' + trigger_created_message.format(trigger_word, trigger_response) 250 | bot.reply_to(m, msg) 251 | save_triggers() 252 | # Ignore private messages. 253 | else: 254 | return 255 | 256 | 257 | # Delete a trigger 258 | @bot.message_handler(commands=['del']) 259 | def delete(m): 260 | # check if this is a bot message replied with /del. 261 | if(len(m.text.split()) == 1 and m.reply_to_message and m.reply_to_message.text): 262 | # Get group's triggers. 263 | trg = get_triggers(m.chat.id) 264 | if(trg): 265 | for x in trg.keys(): 266 | if(trg[x].lower() == m.reply_to_message.text.lower()): 267 | trg.pop(x) 268 | bot.reply_to(m, 'Trigger [%s] deleted.' % x) 269 | return 270 | if(len(m.text.split()) < 2): 271 | bot.reply_to(m, 'Bad Arguments') 272 | return 273 | del_text = m.text.split(' ', 1)[1].strip() 274 | if(m.chat.type in ['group', 'supergroup']): 275 | trg = get_triggers(m.chat.id) 276 | if(trg and del_text in trg.keys()): 277 | trg.pop(del_text) 278 | bot.reply_to(m, 'Trigger [{}] deleted.'.format(del_text)) 279 | save_triggers() 280 | else: 281 | bot.reply_to(m, 'Trigger [{}] not found.'.format(del_text)) 282 | 283 | 284 | @bot.message_handler(commands=['size']) 285 | def size(m): 286 | if(m.chat.type in ['group', 'supergroup']): 287 | trg = get_triggers(m.chat.id) 288 | if(trg): 289 | msg = 'Size of Triggers List = {}'.format(len(trg)) 290 | bot.reply_to(m, msg) 291 | else: 292 | bot.reply_to(m, 'Size of Triggers List = 0') 293 | 294 | 295 | @bot.message_handler(commands=['all']) 296 | def all_triggers(m): 297 | if(m.chat.type in ['group', 'supergroup']): 298 | trg = get_triggers(m.chat.id) 299 | if(trg): 300 | if(len(trg.keys()) == 0): 301 | bot.reply_to(m, 'This group doesn\'t have triggers.') 302 | else: 303 | bot.reply_to(m, 'Triggers:\n' + '\n'.join(trg)) 304 | else: 305 | bot.reply_to(m, 'This group doesn\'t have triggers.') 306 | 307 | 308 | @bot.message_handler(commands=['help', 'start']) 309 | def bot_help(m): 310 | if(m.chat.id == m.from_user.id): 311 | bot.send_message(m.chat.id, full_help, True, parse_mode="Markdown") 312 | else: 313 | bot.send_message(m.chat.id, help_message, True, parse_mode="Markdown") 314 | 315 | 316 | @bot.message_handler(commands=['source']) 317 | def source(m): 318 | if exists(__file__): 319 | bot.send_document(m.chat.id, open(__file__, 'rb')) 320 | else: 321 | bot.reply_to(m, "No source file found :x") 322 | 323 | 324 | @bot.message_handler(commands=['solve']) 325 | def solve(m): 326 | rp = m.reply_to_message 327 | rw = '' 328 | ts = 'Trigger not Found.' 329 | if(len(m.text.split()) >= 2): 330 | rw = m.text.split(' ', 1)[1] 331 | if(rp and rp.from_user.id == bot_id and rp.text): 332 | rw = rp.text 333 | if(m.chat.type in ['group', 'supergroup']): 334 | trg = get_triggers(m.chat.id) 335 | if(trg): 336 | for x in trg.keys(): 337 | if(trg[x].lower() == rw.lower()): 338 | ts = 'Trigger = ' + x 339 | bot.reply_to(m, ts) 340 | 341 | 342 | @bot.message_handler(commands=['about']) 343 | def about(m): 344 | bot.reply_to(m, about_message, parse_mode="Markdown") 345 | 346 | 347 | # END OF COMMAND IMPLEMENTATION SECTION. 348 | 349 | # ADMIN COMMANDS 350 | @bot.message_handler(commands=['broadcast']) 351 | def bcast(m): 352 | if(m.from_user.id != owner): 353 | return 354 | if(len(m.text.split()) == 1): 355 | bot.send_message(m.chat.id, 'No text provided!') 356 | return 357 | count = 0 358 | for g in triggers.keys(): 359 | try: 360 | bot.send_message(int(g), m.text.split(' ', 1)[1]) 361 | count += 1 362 | except ApiException: 363 | continue 364 | bot.send_message(m.chat.id, 'Broadcast sent to {} groups of {}'.format(count, len(triggers.keys()))) 365 | 366 | 367 | @bot.message_handler(commands=['triggers']) 368 | def send_triggers(m): 369 | if(m.from_user.id == owner): 370 | bot.send_document(owner, open('triggers.json')) 371 | 372 | 373 | @bot.message_handler(commands=['gadd']) 374 | def add_global_trigger(m): 375 | if(m.from_user.id == owner): 376 | if(len(m.text.split()) == 1): 377 | bot.reply_to(m, 'Usage:\n/gadd / \n[In reply]:\n/gadd ') 378 | return 379 | if(m.reply_to_message): 380 | if(m.reply_to_message.text): 381 | trigger_response = m.reply_to_message.text 382 | trigger_word = m.text 383 | else: 384 | if(m.text.find(separator, 1) == -1): 385 | bot.reply_to(m, 'Separator not found') 386 | return 387 | rest_text = m.text.split(' ', 1)[1] 388 | trigger_word = u'' + rest_text.split(separator)[0].strip().lower() 389 | trigger_response = u'' + rest_text.split(separator, 1)[1].strip() 390 | if(len(trigger_word) < 4): 391 | bot.reply_to(m, 'Trigger too short. [chars < 4]') 392 | return 393 | if(len(trigger_response) < 1): 394 | bot.reply_to(m, 'Invalid Response.') 395 | return 396 | for g in triggers.keys(): 397 | triggers[g][trigger_word] = trigger_response 398 | bot.reply_to(m, global_trigger_created_message.format(trigger_word, trigger_response)) 399 | save_triggers() 400 | 401 | 402 | @bot.message_handler(commands=['gdel']) 403 | def global_delete(m): 404 | if(m.from_user.id == owner): 405 | if(len(m.text.split()) == 1): 406 | bot.reply_to(m, 'Usage: /gdel ') 407 | else: 408 | trigger_word = m.text.split(' ', 1)[1] 409 | count = 0 410 | for g in triggers.keys(): 411 | if(trigger_word in triggers[g]): 412 | triggers[g].pop(trigger_word) 413 | count += 1 414 | bot.reply_to(m, global_trigger_deleted_message.format(trigger_word, count)) 415 | save_triggers() 416 | 417 | 418 | @bot.message_handler(commands=['gsearch']) 419 | def global_search(m): 420 | if(m.from_user.id == owner): 421 | if(len(m.text.split()) == 1): 422 | bot.reply_to(m, 'Usage: /gsearch ') 423 | else: 424 | trigger_word = m.text.split(' ', 1)[1] 425 | results = [] 426 | for g in triggers.keys(): 427 | if(trigger_word in triggers[g].keys()): 428 | txt = triggers[g][trigger_word] 429 | results.append('[%s]:\n%s' % (g, txt if len(txt) < 30 else txt[:27] + '...')) 430 | if(len(results) == 0): 431 | result_text = 'Trigger not found' 432 | else: 433 | result_text = 'Trigger found in these groups:\n%s' % '\n-----\n'.join(results) 434 | bot.reply_to(m, result_text) 435 | 436 | 437 | @bot.message_handler(commands=['stats']) 438 | def bot_stats(m): 439 | if(m.from_user.id == owner): 440 | total_triggers = 0 441 | for x in triggers.keys(): 442 | total_triggers += len(triggers[x].keys()) 443 | stats_text = 'Chats : {}\nTriggers : {}'.format(len(triggers.keys()), total_triggers) 444 | bot.reply_to(m, stats_text) 445 | 446 | 447 | @bot.message_handler(commands=['clean']) 448 | def clean_triggers(m): 449 | if(m.from_user.id == owner): 450 | group_count = len(triggers) 451 | 452 | total_triggers = 0 453 | for x in triggers.keys(): 454 | total_triggers += len(triggers[x].keys()) 455 | 456 | triggers_count = 0 457 | for g in triggers.copy().keys(): 458 | try: 459 | bot.send_chat_action(g, 'typing') 460 | except: 461 | triggers_count += len(triggers.pop(g)) 462 | 463 | msg_text = ''' 464 | _Original group count_ : *{}* 465 | _Original trigger count_ : *{}* 466 | _Groups deleted_ : *{}* 467 | _Triggers deleted_ : *{}* 468 | _Final group count_ : *{}* 469 | _Final trigger count_ : *{}* 470 | '''.format( 471 | group_count, 472 | total_triggers, 473 | group_count - len(triggers), 474 | triggers_count, 475 | len(triggers), 476 | total_triggers - triggers_count) 477 | save_triggers() 478 | bot.send_message(m.chat.id, msg_text, parse_mode="Markdown") 479 | 480 | 481 | @bot.message_handler(commands=['merge']) 482 | def merge_triggers(m): 483 | if(m.from_user.id == owner): 484 | success_text = 'Triggers merged with [%s], total triggers: [%s]' 485 | no_exists = 'Group %s does not exist in the database.' 486 | if(len(m.text.split()) == 2): 487 | merge_from = int(m.text.split()[1]) 488 | if(get_triggers(merge_from)): 489 | get_triggers(m.chat.id).update(get_triggers(merge_from)) 490 | save_triggers() 491 | bot.reply_to(m, success_text % (merge_from, len(get_triggers(m.chat.id)))) 492 | else: 493 | bot.reply_to(m, no_exists % merge_from) 494 | else: 495 | bot.reply_to(m, 'Missing argument, Group id.') 496 | 497 | 498 | @bot.message_handler(commands=['check_groups']) 499 | def check_groups(m): 500 | if(m.from_user.id == owner): 501 | group_count = 0 502 | for g in triggers.keys(): 503 | try: 504 | bot.send_chat_action(g, 'typing') 505 | group_count += 1 506 | except: 507 | pass 508 | bot.send_message(m.chat.id, 'Working in %s of %s chats' % (group_count, len(triggers))) 509 | 510 | # TRIGGER PROCESSING SECTION. 511 | 512 | 513 | # Triggered when you add the bot to a new group. 514 | @bot.message_handler(content_types=['new_chat_member']) 515 | def invited(m): 516 | if(m.new_chat_member.id == bot_id): 517 | bot.send_message(m.chat.id, invited_message, parse_mode="Markdown") 518 | if(not get_triggers(m.chat.id)): 519 | triggers[str(m.chat.id)] = default_triggers 520 | save_triggers() 521 | bot.send_message(owner, 'Bot added to %s[%s]' % (m.chat.title, m.chat.id)) 522 | 523 | 524 | @bot.message_handler(content_types=['left_chat_member']) 525 | def expulsed(m): 526 | if(m.left_chat_member.id == bot_id): 527 | bot.send_message(owner, 'Bot left chat %s[%s]' % (m.chat.title, m.chat.id)) 528 | 529 | 530 | # Catch every message, for triggers. 531 | @bot.message_handler(func=lambda m: True) 532 | def response(m): 533 | if(not is_recent(m)): 534 | return 535 | if(m.chat.type in ['group', 'supergroup']): 536 | trg = get_triggers(m.chat.id) 537 | if(trg): 538 | for t in trg.keys(): 539 | if t in m.text.lower(): 540 | bot.reply_to(m, trg[t]) 541 | 542 | 543 | # This makes the bot unstoppable :^) 544 | def safepolling(bot): 545 | if(bot.skip_pending): 546 | lid = bot.get_updates()[-1].update_id 547 | else: 548 | lid = 0 549 | while(1): 550 | try: 551 | updates = bot.get_updates(lid + 1, 50) 552 | # print('len updates = %s' % len(updates)) 553 | if(len(updates) > 0): 554 | lid = updates[-1].update_id 555 | bot.process_new_updates(updates) 556 | except ApiException as a: 557 | print(a) 558 | except Exception as e: 559 | print('Exception at %s \n%s' % (asctime(), e)) 560 | now = int(time()) 561 | while(1): 562 | error_text = 'Exception at %s:\n%s' % (asctime(), str(e) if len(str(e)) < 3600 else str(e)[:3600]) 563 | try: 564 | # print('Trying to send message to owner.') 565 | offline = int(time()) - now 566 | bot.send_message(owner, error_text + '\nBot went offline for %s seconds' % offline) 567 | # print('Message sent, returning to polling.') 568 | break 569 | except: 570 | sleep(0.25) 571 | 572 | 573 | if(__name__ == '__main__'): 574 | # Bot starts here. 575 | print('Bot started.') 576 | try: 577 | print('Bot username:[%s]' % bot.get_me().username) 578 | except ApiException: 579 | print('The given token [%s] is invalid, please fix it') 580 | exit(1) 581 | # Tell owner the bot has started. 582 | try: 583 | bot.send_message(owner, 'Bot Started') 584 | except ApiException: 585 | print('''Make sure you have started your bot https://telegram.me/%s. 586 | And configured the owner variable.''' % bot.get_me().username) 587 | exit(1) 588 | print('Safepolling Start.') 589 | safepolling(bot) 590 | 591 | # Nothing beyond this line will be executed. 592 | -------------------------------------------------------------------------------- /TriggerBotMarkov.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import telebot 3 | from peewee import * 4 | from decouple import config 5 | import markovify 6 | import logging 7 | from time import time, asctime, sleep 8 | from telebot.apihelper import ApiException 9 | from telebot.types import InlineKeyboardButton, InlineKeyboardMarkup, Message, User, CallbackQuery 10 | from typing import List 11 | import random 12 | 13 | # VERSION 0.3: removed 100 messages limit, removed chain size and text size(defaults 3 and 1024), added algorithm and group settings. 14 | # VERSION 0.2: removed ignored users array and added chance field on user model, and a settings panel. 15 | # TODO: Inline buttons 16 | __version__ = 0.2 17 | 18 | debug_mode = config('DEBUG', cast=bool) 19 | if(debug_mode): 20 | logging.basicConfig(level=logging.DEBUG) 21 | 22 | # comment to use default timeout. (3.5) 23 | telebot.apihelper.CONNECT_TIMEOUT = 9999 24 | 25 | 26 | markov_algorithms = ["all_messages", "last_message"] 27 | # Define database connector, comment/uncomment what you want to use. 28 | # db = PostgresqlDatabase(config('PG_DTBS'), user=config('PG_USER'), password=config('PG_PASS'), host=config('PG_HOST')) 29 | db = SqliteDatabase('{}.db'.format(__file__)) 30 | # db = MySQLDatabase(config('PG_DTBS'), user=config('PG_USER'), password=config('PG_PASS'), host=config('PG_HOST'), charset='utf8mb4') 31 | 32 | 33 | # Base class, every model inherits the database connector 34 | class BaseModel(Model): 35 | class Meta: 36 | database = db 37 | 38 | 39 | # Telegram user model 40 | class TGUserModel(BaseModel): 41 | # Telegram data 42 | chat_id = CharField() 43 | first_name = CharField() 44 | username = CharField(null=True) 45 | language_code = CharField(default="en") 46 | # Markov settings 47 | # Chances of getting a response automatically. 10/50/90 % /mute for 0 48 | autoreply_chance = IntegerField(default=0) 49 | # Fixed chance of replying after 50/200/1000/5000 messages /mute for 0 50 | autoreply_fixed = IntegerField(default=200) 51 | # True for random chance | False for fixed 52 | random_autoreply = BooleanField(default=True) 53 | markov_algorithm = CharField(default="last_message", choices=markov_algorithms) 54 | 55 | 56 | # Model to store all messages 57 | class UserMessageModel(BaseModel): 58 | user = ForeignKeyField(TGUserModel, backref='messages') 59 | message_id = CharField() 60 | chat_id = CharField() 61 | message_text = CharField(max_length=4000) 62 | 63 | 64 | class GeneratedMessageModel(BaseModel): 65 | user = ForeignKeyField(TGUserModel, backref='generated_messages') 66 | chat_id = CharField(default="") 67 | message_text = CharField(max_length=1000) 68 | 69 | 70 | # Store group admins and settings. 71 | class GroupSettings(BaseModel): 72 | user = ForeignKeyField(TGUserModel) 73 | chat_id = CharField() 74 | override_settings = BooleanField(default=False) 75 | admins = ManyToManyField(TGUserModel, backref="groups") 76 | 77 | 78 | # Create database file if it doesn't exists. 79 | db.create_tables([TGUserModel, UserMessageModel, GeneratedMessageModel, GroupSettings, 80 | GroupSettings.admins.get_through_model()], safe=True) 81 | 82 | # Bot instance initialization. 83 | bot = telebot.TeleBot(config('BOT_TOKEN')) 84 | 85 | # Bot starts here. 86 | logging.info('Bot started.') 87 | try: 88 | bot_info = bot.get_me() 89 | logging.debug('{bot_info.first_name} @{bot_info.username}[{bot_info.id}]'.format(bot_info=bot_info)) 90 | except ApiException: 91 | logging.critical('The given token [{0}] is invalid, please fix it'.format(config('BOT_TOKEN'))) 92 | exit(1) 93 | 94 | 95 | # Return a TGUser instance based on telegram message instance. 96 | def get_user_from_message(message: telebot.types.Message) -> TGUserModel: 97 | if(message.forward_from): 98 | logging.debug("Received a forwarded message.") 99 | user = message.forward_from # If it was forwarder from a user, return forwarded message's user. 100 | elif(message.reply_to_message): 101 | logging.debug("Received a replied message.") 102 | user = message.reply_to_message.from_user # If reply, user is resolved from the replied message. 103 | else: 104 | logging.debug("Received a normal message.") 105 | user: User = message.from_user # Resolve user as message sender 106 | # Search user on database or create if it doesn't exists 107 | logging.debug("Using user {user.id} from message {message.message_id}".format(user=user, message=message)) 108 | try: 109 | db_user: TGUserModel = TGUserModel.get(chat_id=user.id) 110 | # Update/Fill nickname and username 111 | except DoesNotExist: 112 | logging.debug("Creating user {user.id} {user.first_name} {user.username} {user.language_code}".format(user=user)) 113 | db_user = TGUserModel.create( 114 | chat_id=user.id, 115 | first_name=user.first_name, 116 | username=user.username if user.username else None, 117 | language_code=user.language_code if user.language_code else 'en') 118 | return db_user 119 | 120 | 121 | def get_group_from_message(message: Message) -> GroupSettings: 122 | # Create TGUser with group info. (YES i store chat groups as users) 123 | group_obj, created = TGUserModel.get_or_create(chat_id=message.chat.id, first_name=message.chat.title, username=message.chat.username) 124 | try: 125 | group_settings: GroupSettings = GroupSettings.get(GroupSettings.user == group_obj) 126 | except DoesNotExist: 127 | group_settings = GroupSettings.create(user=group_obj, chat_id=message.chat.id) 128 | admins_id = [admin.user.id for admin in bot.get_chat_administrators(message.chat.id)] 129 | group_settings.admins = TGUserModel.select().where(TGUserModel.chat_id.in_(admins_id)) 130 | return group_settings 131 | 132 | 133 | # Add every text message to the database 134 | def text_model_processor(messages: List[Message]): 135 | # logging.info("Processing %s new messages", len(messages)) 136 | data_source = [] 137 | for message in messages: 138 | # Only process text messages that are not commands and contains at least 3 words. 139 | if message.content_type == "text" and not message.text.startswith('/') and message.text.count(' ') >= 2: 140 | user = get_user_from_message(message) 141 | data_source.append({ 142 | 'user': user, 'message_text': message.text.lower(), 143 | 'message_id': message.message_id, 'chat_id': message.chat.id 144 | }) 145 | # UserMessageModel.create(user=user, message_text=message.text).save() 146 | logging.info("Saving {} text messages to the database".format(len(data_source))) 147 | if(data_source): 148 | with db.atomic(): 149 | UserMessageModel.insert_many(data_source).execute() 150 | 151 | 152 | bot.set_update_listener(text_model_processor) 153 | 154 | # GLOBAL MESSAGES SECTION. 155 | about_message = ''' 156 | TriggerBot *%s* 157 | [Source Code on Github](https://github.com/sanguchi/TriggerBot/) 158 | [Give me 5 Stars](https://telegram.me/storebot?start=TriggerResponseBot) 159 | Author: @Pekatarina 160 | ''' % __version__ 161 | 162 | bot_help_text = ''' 163 | Hi, i'm *Trigger!*, i can learn from people's messages and chat like them. 164 | If you want me to generate a message, you can mention me, say my nickname, reply to any of my messages, or use: 165 | `/trigger` 166 | ''' 167 | 168 | settings_text = ''' 169 | {user.first_name}[{user.chat_id}]: 170 | Messages sent: {messages} | generated: {generated} 171 | Markov Algorithm: {algorithm} | Autoreply: {method}|{value} 172 | ''' 173 | 174 | # When bot is added to group, get all admins and add them to internal admin list for this group 175 | @bot.message_handler(content_types=['new_chat_members']) 176 | def bot_added_to_chat(message: Message): 177 | if(any([user.id == bot_info.id for user in message.new_chat_members])): 178 | logging.info("Bot added to group {message.chat.id} {message.chat.title} by {message.from_user.id} {message.from_user.first_name}".format(message=message)) 179 | get_group_from_message(message) 180 | 181 | 182 | def is_private_chat(message: Message): 183 | return message.chat.type == 'private' 184 | 185 | 186 | @bot.message_handler(commands=['start', 'help'], func=is_private_chat) 187 | def greet_user(message: Message): 188 | bot.reply_to(message, bot_help_text, parse_mode="Markdown") 189 | 190 | 191 | @bot.message_handler(commands=['about'], func=is_private_chat) 192 | def about(message: Message): 193 | bot.reply_to(message, about_message, parse_mode="Markdown") 194 | 195 | 196 | def get_statistics(user_obj: TGUserModel): 197 | message_count = user_obj.messages.count() 198 | generated_messages = user_obj.generated_messages.count() 199 | algorithm = user_obj.markov_algorithm 200 | method = "Random" if user_obj.random_autoreply else "Fixed" 201 | value = user_obj.autoreply_chance if user_obj.random_autoreply else user_obj.autoreply_fixed 202 | return settings_text.format( 203 | user=user_obj, messages=message_count, generated=generated_messages, 204 | algorithm=algorithm, method=method, value=value 205 | ) 206 | 207 | 208 | def generate_settings_keyboard(user_obj: TGUserModel): 209 | keyboard = InlineKeyboardMarkup(4) 210 | if(user_obj.random_autoreply): 211 | autoreply_type: InlineKeyboardButton = InlineKeyboardButton('Change autoreply to fixed', None, 'autoreply') 212 | chances: List[InlineKeyboardButton] = [ 213 | InlineKeyboardButton(str(chance), None, str(chance)) for chance in [0, 10, 50, 90] 214 | ] 215 | else: 216 | autoreply_type: InlineKeyboardButton = InlineKeyboardButton('Change autoreply to random', None, 'autoreply') 217 | chances: List[InlineKeyboardButton] = [ 218 | InlineKeyboardButton(str(chance), None, str(chance)) for chance in [0, 20, 200, 2000] 219 | ] 220 | keyboard.add(autoreply_type) 221 | keyboard.add(*chances) 222 | keyboard.add(InlineKeyboardButton("Markov Algorithm", None, 'algorithm'), InlineKeyboardButton("X", None, "close")) 223 | return keyboard 224 | 225 | 226 | def get_group_statistics(group: GroupSettings): 227 | message_count = UserMessageModel.select().where(UserMessageModel.chat_id==group.chat_id).count() 228 | generated_messages = GeneratedMessageModel.select().where(GeneratedMessageModel.chat_id == group.chat_id).count() 229 | algorithm = group.user.markov_algorithm 230 | method = "Random" if group.user.random_autoreply else "Fixed" 231 | value = group.user.autoreply_chance if group.user.random_autoreply else group.user.autoreply_fixed 232 | return settings_text.format( 233 | user=group.user, messages=message_count, generated=generated_messages, 234 | algorithm=algorithm, method=method, value=value 235 | ) + "\nOverride User Settings: {}".format("YES" if group.override_settings else "NO") 236 | 237 | 238 | def group_keyboard(group: GroupSettings, admin: TGUserModel): 239 | keyboard = InlineKeyboardMarkup(4) 240 | if(group.user.random_autoreply): 241 | autoreply_type: InlineKeyboardButton = InlineKeyboardButton('Change autoreply to fixed', None, 'group_autoreply') 242 | chances: List[InlineKeyboardButton] = [ 243 | InlineKeyboardButton(str(chance), None, 'group_{}'.format(chance)) for chance in [0, 10, 50, 90] 244 | ] 245 | else: 246 | autoreply_type: InlineKeyboardButton = InlineKeyboardButton('Change autoreply to random', None, 'group_autoreply') 247 | chances: List[InlineKeyboardButton] = [ 248 | InlineKeyboardButton(str(chance), None, 'group_{}'.format(chance)) for chance in [0, 20, 200, 2000] 249 | ] 250 | override_button: InlineKeyboardButton = InlineKeyboardButton('Toggle Override User Settings', None, 'group_override') 251 | keyboard.add(autoreply_type) 252 | keyboard.add(*chances) 253 | keyboard.add(InlineKeyboardButton("Markov Algorithm", None, 'group_algorithm'), override_button) 254 | keyboard.add(InlineKeyboardButton("See personal settings", None, "personal_{}".format(admin.chat_id)), InlineKeyboardButton("X", None, "close")) 255 | return keyboard 256 | 257 | 258 | @bot.message_handler(commands=['settings']) 259 | def send_user_statistics(message: Message): 260 | user_obj: TGUserModel = get_user_from_message(message) 261 | keyboard: InlineKeyboardMarkup = None 262 | stats: str = get_statistics(user_obj) 263 | # If private chat 264 | if(is_private_chat(message)): 265 | logging.debug('Settings in private chat') 266 | keyboard: InlineKeyboardMarkup = generate_settings_keyboard(user_obj) 267 | if(message.chat.type in ['group', 'supergroup']): 268 | group: GroupSettings = GroupSettings.get(GroupSettings.chat_id == message.chat.id) 269 | if (user_obj in group.admins): 270 | stats = get_group_statistics(group) 271 | keyboard = group_keyboard(group, user_obj) 272 | bot.reply_to(message, stats, reply_markup=keyboard) 273 | 274 | 275 | # Mute is shortcut to setting all chances to zero, for groups and users. 276 | @bot.message_handler(commands=['mute']) 277 | def mute_bot(message: Message): 278 | user_obj: TGUserModel = get_user_from_message(message) 279 | if(message.chat.type in ['group', 'supergroup']): 280 | group: GroupSettings = GroupSettings.get(GroupSettings.chat_id == message.chat.id) 281 | if (user_obj in group.admins): 282 | group.override_settings = True 283 | group.user.autoreply_chance = 0 284 | group.user.autoreply_fixed = 0 285 | group.save() 286 | bot.reply_to(message, "I will not reply randomly anymore.") 287 | else: 288 | user_obj.autoreply_chance = 0 289 | user_obj.autoreply_fixed = 0 290 | bot.reply_to(message, "I will not randomly reply to your messages anymore.") 291 | else: 292 | user_obj.autoreply_chance = 0 293 | user_obj.autoreply_fixed = 0 294 | bot.reply_to(message, "I will not randomly reply to your messages anymore.") 295 | user_obj.save() 296 | 297 | 298 | # Message fetching algorithm 299 | def fetch_all_messages(user: TGUserModel, group: GroupSettings = None): 300 | if(group is None): 301 | logging.debug("Fetching {} messages from user {}".format(user.messages.count(), user.chat_id)) 302 | return [message.message_text for message in user.messages] 303 | chat_messages = UserMessageModel.select().where(UserMessageModel.chat_id == group.chat_id) 304 | logging.debug("Fetching {} messages from chat {}".format(chat_messages.count(), group.chat_id)) 305 | return [message.message_text for message in chat_messages] 306 | 307 | 308 | def fetch_latest_messages(user: TGUserModel, group: GroupSettings = None): 309 | if(group is None): 310 | logging.debug("Fetching {} messages from user {}".format(user.messages.limit(100).count(), user.chat_id)) 311 | return [message.message_text for message in user.messages.limit(100)] 312 | chat_messages = UserMessageModel.select().where(UserMessageModel.chat_id == group.chat_id).limit(100) 313 | logging.debug("Fetching {} messages from chat {}".format(chat_messages.count(), group.chat_id)) 314 | return [message.message_text for message in chat_messages] 315 | 316 | 317 | def fetch_all_included(user: TGUserModel, keyword: str, group: GroupSettings = None): 318 | if(group is None): 319 | selected_messages = UserMessageModel.select().where((UserMessageModel.user == user) & (UserMessageModel.message_text.contains(keyword))) 320 | logging.debug("Fetching {} messages from user {}".format(selected_messages.count(), user.chat_id)) 321 | return [message.message_text for message in selected_messages] 322 | selected_messages = UserMessageModel.select().where((UserMessageModel.chat_id == group.chat_id) & UserMessageModel.message_text.contains(keyword)) 323 | logging.debug("Fetching {} messages from chat {}".format(selected_messages.count(), group.chat_id)) 324 | return [message.message_text for message in selected_messages] 325 | 326 | 327 | def fetch_messages(user: TGUserModel, group: GroupSettings = None, keyword: str = None) -> List[str]: 328 | if(keyword): 329 | return fetch_all_included(user, keyword, group) 330 | if(user.markov_algorithm == "last_message"): 331 | return fetch_latest_messages(user, group) 332 | else: 333 | return fetch_all_messages(user, group) 334 | 335 | 336 | # Tries to return a short dumb response, if it already exists, return none 337 | def generate_markov(messages: List[str]) -> str: 338 | if(not messages): 339 | return None 340 | # More messages from db means more complex responses. 341 | if(len(messages) < 100): 342 | state_size = 1 343 | elif(500 > len(messages) >= 100): 344 | state_size = 2 345 | else: 346 | state_size = 3 347 | text_model = markovify.NewlineText(messages, state_size=state_size) 348 | result = text_model.make_short_sentence(1024) 349 | return result 350 | 351 | 352 | def check_duplicated(message_text: str, user: TGUserModel, group: GroupSettings = None): 353 | chat_id = user.chat_id 354 | if(group): 355 | chat_id = group.chat_id 356 | response, created = GeneratedMessageModel.get_or_create(user=user, message_text=message_text, chat_id=chat_id) 357 | if(created): 358 | logging.debug("New message: %s" % response.message_text) 359 | return not created 360 | 361 | 362 | @bot.message_handler(commands=['trigger']) 363 | def trigger_bot(message: Message): 364 | keyword = message.text.split(' ') 365 | user_obj: TGUserModel = get_user_from_message(message) 366 | group = get_group_from_message(message) if message.chat.type != 'private' else None 367 | keyword = ' '.join(keyword[1:]) if(len(keyword) >= 2) else None 368 | generated_message = generate_markov(fetch_messages(user_obj, group, keyword)) 369 | if(generated_message and not check_duplicated(generated_message, user_obj, group)): 370 | bot.reply_to(message, generated_message) 371 | else: 372 | bot.reply_to(message, "Cannot generate a message, please talk more.", parse_mode="HTML") 373 | 374 | 375 | def should_reply(user: TGUserModel, group: GroupSettings = None) -> bool: 376 | if(group and group.override_settings): 377 | if(group.user.random_autoreply and group.user.autoreply_chance is not 0): 378 | chances = random.randint(1, 100) 379 | logging.debug("random number: {} autoreply chance: {}".format(chances, group.user.autoreply_chance)) 380 | return chances <= group.user.autoreply_chance 381 | else: 382 | counter = UserMessageModel.select().where(UserMessageModel.chat_id == group.chat_id).count() 383 | logging.debug("message counter: {} autoreply fixed: {}".format(counter, group.user.autoreply_fixed)) 384 | return counter % group.user.autoreply_fixed is 0 if group.user.autoreply_fixed is not 0 else False 385 | else: 386 | if (user.random_autoreply and user.autoreply_chance is not 0): 387 | chances = random.randint(1, 100) 388 | logging.debug("random number: {} autoreply chance: {}".format(chances, user.autoreply_chance)) 389 | return chances <= user.autoreply_chance 390 | else: 391 | counter = user.messages.count() 392 | logging.debug("message counter: {} autoreply fixed: {}".format(counter, user.autoreply_fixed)) 393 | return counter % user.autoreply_fixed is 0 if user.autoreply_fixed is not 0 else False 394 | 395 | 396 | @bot.message_handler(regexp='{}|{}'.format(bot_info.first_name, bot_info.username)) 397 | def reply_on_mention(message: Message): 398 | user_obj: TGUserModel = get_user_from_message(message) 399 | group: GroupSettings = get_group_from_message(message) if message.chat.type != 'private' else None 400 | if(group and group.override_settings): # Use group's settings 401 | settings: TGUserModel = group.user 402 | else: 403 | group = None 404 | settings = user_obj 405 | generated_message = generate_markov(fetch_messages(settings, group)) 406 | if (generated_message and not check_duplicated(generated_message, user_obj, group)): 407 | bot.reply_to(message, generated_message) 408 | 409 | 410 | @bot.message_handler(content_types=['text'], func=lambda m: m.reply_to_message and m.reply_to_message.from_user.id == bot_info.id) 411 | def reply_on_reply(message: Message): 412 | reply_on_mention(message) # I'm not lazy, i just realized that i was about to rewrite that function again but only for replies. 413 | 414 | 415 | # Try to reply to every text message 416 | @bot.message_handler(content_types=['text']) 417 | def reply_intent(message: Message): 418 | user_obj: TGUserModel = get_user_from_message(message) 419 | group: GroupSettings = get_group_from_message(message) if message.chat.type != 'private' else None 420 | if (group and group.override_settings): # Use group's settings 421 | settings: TGUserModel = group.user 422 | else: 423 | group = None 424 | settings = user_obj 425 | if(should_reply(settings, group)): 426 | generated_message = generate_markov(fetch_messages(settings, group)) 427 | if (generated_message and not check_duplicated(generated_message, user_obj, group)): 428 | bot.reply_to(message, generated_message) 429 | 430 | 431 | def get_user_from_callback(query: CallbackQuery) -> TGUserModel: 432 | try: 433 | db_user: TGUserModel = TGUserModel.get(chat_id=query.from_user.id) 434 | except DoesNotExist: 435 | db_user = TGUserModel.create( 436 | chat_id=query.from_user.id, 437 | first_name=query.from_user.first_name.lower(), 438 | username=query.from_user.username.lower() if query.from_user.username else None, 439 | language_code=query.from_user.language_code) 440 | return db_user 441 | 442 | # INLINE BUTTONS 443 | @bot.callback_query_handler(func=lambda q: q.data in ["0", "10", "20", "50", "90", "200", "2000"]) 444 | def set_autoreply_chance(query: CallbackQuery): 445 | db_user: TGUserModel = get_user_from_callback(query) 446 | if(db_user.random_autoreply): 447 | db_user.autoreply_chance = int(query.data) 448 | bot.answer_callback_query(query.id, "Random chances set to {}%".format(query.data)) 449 | else: 450 | db_user.autoreply_fixed = int(query.data) 451 | bot.answer_callback_query(query.id, "Fixed chances set to {}%".format(query.data)) 452 | db_user.save() 453 | 454 | bot.edit_message_text( 455 | get_statistics(db_user), 456 | query.from_user.id, 457 | query.message.message_id, 458 | reply_markup=generate_settings_keyboard(db_user)) 459 | 460 | 461 | @bot.callback_query_handler(func=lambda q: q.data == 'close') 462 | def close_settings_keyboard(query: CallbackQuery): 463 | bot.edit_message_reply_markup(query.message.chat.id, query.message.message_id, reply_markup=None) 464 | 465 | 466 | @bot.callback_query_handler(func=lambda q: q.data == 'autoreply') 467 | def toggle_autoreply_type(query: CallbackQuery): 468 | db_user: TGUserModel = get_user_from_callback(query) 469 | db_user.random_autoreply = not db_user.random_autoreply 470 | db_user.save() 471 | bot.edit_message_text( 472 | get_statistics(db_user), 473 | query.from_user.id, 474 | query.message.message_id, 475 | reply_markup=generate_settings_keyboard(db_user)) 476 | 477 | 478 | @bot.callback_query_handler(func=lambda q: q.data == 'algorithm') 479 | def toggle_fetch_algorithm(query: CallbackQuery): 480 | db_user: TGUserModel = get_user_from_callback(query) 481 | logging.debug("user {} alg: {}".format(db_user.chat_id, db_user.markov_algorithm)) 482 | db_user.markov_algorithm = "last_message" if db_user.markov_algorithm == "all_messages" else "all_messages" 483 | db_user.save() 484 | bot.edit_message_text( 485 | get_statistics(db_user), 486 | query.from_user.id, 487 | query.message.message_id, 488 | reply_markup=generate_settings_keyboard(db_user)) 489 | 490 | # GROUP INLINE BUTTONS FOR ADMINS 491 | @bot.callback_query_handler(func=lambda q: q.data.startswith('group_') and q.data.split('_')[1] in ["0", "10", "20", "50", "90", "200", "2000"]) 492 | def set_autoreply_chance(query: CallbackQuery): 493 | user_obj: TGUserModel = get_user_from_callback(query) 494 | group: GroupSettings = get_group_from_message(query.message) 495 | chances = int(query.data.split('_')[1]) 496 | if (user_obj in group.admins): 497 | if(group.user.random_autoreply): 498 | group.user.autoreply_chance = chances 499 | bot.answer_callback_query(query.id, "Random chances set to {}%".format(chances)) 500 | else: 501 | group.user.autoreply_fixed = chances 502 | bot.answer_callback_query(query.id, "Fixed chances set to {}%".format(chances)) 503 | group.user.save() 504 | bot.edit_message_text( 505 | get_group_statistics(group), 506 | query.message.chat.id, 507 | query.message.message_id, 508 | reply_markup=group_keyboard(group, user_obj)) 509 | else: 510 | bot.answer_callback_query(query.id) 511 | 512 | 513 | @bot.callback_query_handler(func=lambda q: q.data == "group_override") 514 | def toggle_group_override(query: CallbackQuery): 515 | user_obj: TGUserModel = get_user_from_message(query.message) 516 | group: GroupSettings = get_group_from_message(query.message) 517 | if (user_obj in group.admins): 518 | group.override_settings = not group.override_settings 519 | group.save() 520 | bot.edit_message_text( 521 | get_group_statistics(group), 522 | query.message.chat.id, 523 | query.message.message_id, 524 | reply_markup=group_keyboard(group, user_obj)) 525 | 526 | 527 | @bot.callback_query_handler(func=lambda q: q.data == 'group_algorithm') 528 | def toggle_group_fetch_algorithm(query: CallbackQuery): 529 | user_obj: TGUserModel = get_user_from_message(query.message) 530 | group: GroupSettings = get_group_from_message(query.message) 531 | if (user_obj in group.admins): 532 | group.user.markov_algorithm = "last_message" if group.user.markov_algorithm == "all_messages" else "all_messages" 533 | group.user.save() 534 | bot.edit_message_text( 535 | get_group_statistics(group), 536 | query.message.chat.id, 537 | query.message.message_id, 538 | reply_markup=group_keyboard(group, user_obj)) 539 | 540 | 541 | @bot.callback_query_handler(func=lambda q: q.data == "group_autoreply") 542 | def toggle_group_autoreply_type(query: CallbackQuery): 543 | user_obj: TGUserModel = get_user_from_message(query.message) 544 | group: GroupSettings = get_group_from_message(query.message) 545 | if(user_obj in group.admins): 546 | group.user.random_autoreply = not group.user.random_autoreply 547 | group.user.save() 548 | bot.edit_message_text( 549 | get_group_statistics(group), 550 | query.message.chat.id, 551 | query.message.message_id, 552 | reply_markup=group_keyboard(group, user_obj)) 553 | else: 554 | bot.answer_callback_query(query.id) 555 | 556 | 557 | @bot.callback_query_handler(func=lambda q: q.data.startswith("personal_")) 558 | def show_user_statistics(query: CallbackQuery): 559 | user_obj: TGUserModel = TGUserModel.get(TGUserModel.chat_id == query.data.split("_")[1]) 560 | bot.edit_message_text( 561 | get_statistics(user_obj), 562 | query.message.chat.id, 563 | query.message.message_id, 564 | reply_markup=None) 565 | 566 | 567 | def notify_exceptions(exception_instance: Exception): 568 | logging.warning('Exception at %s \n%s', asctime(), exception_instance) 569 | now = int(time()) 570 | logging.debug('Trying to send exception message to owner.') 571 | while (1): 572 | error_text = 'Exception at %s:\n%s' % ( 573 | asctime(), 574 | str(exception_instance) if len(str(exception_instance)) < 3600 else str(exception_instance)[:3600]) 575 | try: 576 | offline_time = int(time()) - now 577 | bot.send_message(config('OWNER_ID'), error_text + '\nBot went offline for %s seconds' % offline_time) 578 | logging.debug('Message sent, returning to polling.') 579 | break 580 | except: 581 | sleep(3) 582 | 583 | 584 | # This makes the bot unstoppable :^) 585 | def safepolling(): 586 | if(bot.skip_pending): 587 | last_update_id = bot.get_updates()[-1].update_id 588 | else: 589 | last_update_id = 0 590 | while(1): 591 | logging.debug("Getting updates using update id %s", last_update_id) 592 | try: 593 | updates = bot.get_updates(last_update_id + 1, 50) 594 | logging.debug('Fetched %s updates', len(updates)) 595 | if(len(updates) > 0): 596 | last_update_id = updates[-1].update_id 597 | bot.process_new_updates(updates) 598 | except ApiException as api_exception: 599 | logging.warning(api_exception) 600 | except Exception as exception_instance: 601 | if(debug_mode): 602 | notify_exceptions(exception_instance) 603 | 604 | 605 | if(__name__ == '__main__'): 606 | # Tell owner the bot has started. 607 | bot.remove_webhook() 608 | if(debug_mode): 609 | try: 610 | bot.send_message(config('OWNER_ID'), 'Bot Started') 611 | except ApiException: 612 | logging.critical('''Make sure you have started your bot https://telegram.me/%s. 613 | And configured the owner variable.''' % bot_info.username) 614 | exit(1) 615 | logging.info('Safepolling Start.') 616 | safepolling() 617 | # Nothing beyond this line will be executed. 618 | -------------------------------------------------------------------------------- /TriggerBotSqlite.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import argparse 3 | import re 4 | from time import time, asctime, sleep 5 | import tempfile 6 | from peewee import * 7 | import telebot 8 | from telebot.apihelper import ApiException 9 | 10 | __version__ = 0.1 11 | 12 | # database section 13 | db = SqliteDatabase('{}.db'.format(__file__)) 14 | 15 | # Set this to False to check the database for every message. 16 | # This setting does not affect the /lock, /del and /add commands. 17 | triggers_dict = False 18 | 19 | 20 | class BaseModel(Model): 21 | class Meta: 22 | database = db 23 | 24 | 25 | class TGUserModel(BaseModel): 26 | chat_id = CharField() 27 | first_name = CharField() 28 | username = CharField(null=True) 29 | 30 | 31 | class ConfigModel(BaseModel): 32 | bot_user = ForeignKeyField(TGUserModel) 33 | token = CharField() 34 | owner = IntegerField() 35 | 36 | 37 | class TextTriggerModel(BaseModel): 38 | chat = ForeignKeyField(TGUserModel, 'chat_triggers') 39 | trigger_text = CharField(max_length=3000) 40 | response_text = CharField(max_length=3000) 41 | locked_by = IntegerField(default=0) 42 | 43 | 44 | # TODO: implement media triggers. 45 | class MediaTriggerModel(BaseModel): 46 | chat = ForeignKeyField(TGUserModel, 'media_triggers') 47 | trigger_text = CharField(max_length=3000) 48 | file_id = CharField() 49 | file_type = CharField() 50 | locked_by = IntegerField(default=0) 51 | 52 | 53 | db.create_tables([TGUserModel, TextTriggerModel, MediaTriggerModel, ConfigModel], safe=True) 54 | 55 | 56 | # Check command line arguments, -t --token , -o --owner . 57 | def check_args(): 58 | print("checking args") 59 | parser = argparse.ArgumentParser() 60 | parser.add_argument('-t', '--token', help="Token received from botfather.") 61 | parser.add_argument('-o', '--owner', help="Telegram chat ID to send status messages.", type=int) 62 | parser.add_argument('-d', '--dict', help="Keep database as a dict object stored in ram", default=False) 63 | args = parser.parse_args() 64 | if(args.dict): 65 | global triggers_dict 66 | triggers_dict = True 67 | if (args.token): 68 | if (args.owner): 69 | try: 70 | dummy_bot = telebot.TeleBot(args.token) 71 | bot_info = dummy_bot.get_me() 72 | bot_user, created = TGUserModel.get_or_create( 73 | chat_id=bot_info.id, 74 | first_name=bot_info.first_name, 75 | username=bot_info.username 76 | ) 77 | bot_user.save() 78 | try: 79 | dummy_bot.send_message(args.owner, "This bot is ready!") 80 | bot_cfg = ConfigModel.create(bot_user=bot_user, token=args.token, owner=args.owner) 81 | bot_cfg.save() 82 | except ApiException as ae: 83 | print('''Make sure you have started your bot https://telegram.me/{}. 84 | And configured the owner variable. 85 | ApiException: {}'''.format(bot_info.username, ae)) 86 | exit(1) 87 | except ApiException as ApiError: 88 | print("Invalid token[{}]: {}".format(args.token, ApiError)) 89 | exit(1) 90 | except Exception as e: 91 | print(e) 92 | exit(1) 93 | else: 94 | print("Owner ID not supplied") 95 | exit(1) 96 | 97 | 98 | if(ConfigModel.select().count() == 0): 99 | check_args() 100 | try: 101 | bot_instance_from_db = ConfigModel.get() 102 | except DoesNotExist: 103 | print("First create your bot with:\n{} --token --owner ".format(__file__)) 104 | exit(1) 105 | 106 | bot = telebot.TeleBot(bot_instance_from_db.token) 107 | owner = bot_instance_from_db.owner 108 | 109 | 110 | # Define a custom Listener to print messages to console. 111 | 112 | # Python2 version 113 | def listener2(messages): 114 | for m in messages: 115 | cid = m.chat.id 116 | name = m.from_user.first_name.encode('ascii', 'ignore').decode('ascii') 117 | if(m.content_type == 'text'): 118 | message_text = m.text.encode('ascii', 'ignore').decode('ascii') 119 | else: 120 | message_text = m.content_type 121 | print('{}[{}]:{}'.format(name, cid, message_text)) 122 | 123 | 124 | # Python3 version. 125 | def listener3(messages): 126 | for m in messages: 127 | print('%s[%s]:%s' % (m.from_user.first_name, m.chat.id, m.text if m.text else m.content_type)) 128 | 129 | # Change to listener2 if this complains about encoding. 130 | bot.set_update_listener(listener3) 131 | 132 | help_message = ''' 133 | You need help! 134 | *Commands:* 135 | `/add / ` 136 | |-_Adds a new trigger._ 137 | `/del ` 138 | |-_deletes trigger if exists._ 139 | `/lock ` 140 | |-_locks trigger if exists._ 141 | *For a detailed help send /help in private.* 142 | ''' 143 | 144 | full_help_message = ''' 145 | You really need help! 146 | *Main Functions:* 147 | *Add Triggers:* 148 | `/add / ` 149 | Example: 150 | `/add Hello / Hello there!` 151 | Also works via reply. 152 | _Just send_ `/add ` _in Reply to Another Message:_ 153 | *Delete Triggers:* 154 | `/del ` 155 | Deletes a defined trigger, example: 156 | `/del hello` 157 | Also works via reply. 158 | _Just send_ `/del` _in Reply to a bot's Message:_ 159 | *Lock/Unlock Triggers:* 160 | `/lock ` 161 | Locks a trigger so nobody can delete it unless you unlock it, example: 162 | `/lock hello` 163 | Also works via reply. 164 | _Just send_ `/lock` _in Reply to a bot's Message:_ 165 | *Misc:* 166 | /size 167 | _Returns size of triggers list._ 168 | /all 169 | _List all triggers._ 170 | /help 171 | _This message._ 172 | /source 173 | _Sends source code TriggerBot.py_ 174 | /solve 175 | _Resolve what trigger causes the given response, if exists._ 176 | *Also works by reply:* 177 | _Just send_ `/solve` _in Reply to a bot's Message:_ 178 | /about 179 | _About this bot._ 180 | ''' 181 | 182 | 183 | def db2dict(): 184 | global triggers_dict 185 | if(triggers_dict): 186 | triggers_dict = {} 187 | for chat in TGUserModel.select(): 188 | triggers_list = dict() 189 | for trigger in chat.chat_triggers: 190 | triggers_list[trigger.trigger_text] = trigger.response_text 191 | triggers_dict[chat.chat_id] = triggers_list 192 | print(chat.chat_id, "loaded", len(triggers_dict[chat.chat_id]), "triggers.") 193 | print("Loaded ", len(triggers_dict.keys()), "chats.") 194 | db2dict() 195 | 196 | 197 | def get_chat_from_message(msg): 198 | try: 199 | chat = TGUserModel.get(TGUserModel.chat_id == msg.chat.id) 200 | except DoesNotExist: 201 | chat = TGUserModel.create( 202 | chat_id=msg.chat.id, 203 | first_name=msg.chat.first_name if msg.chat.first_name else msg.chat.title, 204 | username=msg.chat.username) 205 | chat.save() 206 | return chat 207 | 208 | 209 | @bot.message_handler(regexp=r'/add .{4,3000}/.{1,3000}') 210 | def add_trigger(msg): 211 | trigger, response = re.search(r'/add(.{5,3000})/(.{1,3000})', msg.text).groups() 212 | chat = get_chat_from_message(msg) 213 | trigger, created = TextTriggerModel.get_or_create( 214 | chat=chat, 215 | trigger_text=trigger.lower().strip(), 216 | response_text=response.strip()) 217 | if(created): 218 | trigger.save() 219 | bot.reply_to(msg, "Trigger created.") 220 | db2dict() 221 | else: 222 | if(trigger.locked_by): 223 | who_locked = TGUserModel.get(TGUserModel.chat_id == trigger.locked_by) 224 | bot.reply_to(msg, "You can't override this trigger, it's locked by {}".format( 225 | '@' + who_locked.username if who_locked.username else '{}[{}]'.format( 226 | who_locked.first_name, who_locked.chat_id))) 227 | else: 228 | trigger.response_text = response.strip() 229 | trigger.save() 230 | bot.reply_to(msg, "Trigger saved.") 231 | db2dict() 232 | 233 | 234 | @bot.message_handler(func=lambda m: m.reply_to_message, regexp=r'/add .{4,3000}') 235 | def add_trigger_on_reply(msg): 236 | if(msg.reply_to_message.text): 237 | reply_len = len(msg.reply_to_message.text) 238 | if(0 < reply_len < 3000): 239 | chat = get_chat_from_message(msg) 240 | trigger_text = msg.text.split(' ', 1)[1].strip().lower() 241 | response_text = msg.reply_to_message.text 242 | try: 243 | trigger = TextTriggerModel.get( 244 | TextTriggerModel.chat == chat, 245 | TextTriggerModel.trigger_text == trigger_text) 246 | if(trigger.locked_by): 247 | who_locked = TGUserModel.get(TGUserModel.chat_id == trigger.locked_by) 248 | bot.reply_to(msg, "You can't override this trigger, it's locked by {}".format( 249 | '@' + who_locked.username if who_locked.username else '{}[{}]'.format( 250 | who_locked.first_name, who_locked.chat_id))) 251 | else: 252 | trigger.response_text = response_text 253 | trigger.save() 254 | bot.reply_to(msg, "Trigger response modified.") 255 | except DoesNotExist: 256 | trigger = TextTriggerModel.create(chat=chat, trigger_text=trigger_text, response_text=response_text) 257 | bot.reply_to(msg, "Trigger created.") 258 | trigger.save() 259 | db2dict() 260 | else: 261 | bot.reply_to(msg, "Response text too long: length is greater than 3000 characters.") 262 | else: 263 | bot.reply_to(msg, "Media responses are coming soon.") 264 | 265 | 266 | @bot.message_handler(regexp=r'/del .{4,3000}') 267 | def del_trigger(msg): 268 | chat = get_chat_from_message(msg) 269 | trigger_text = msg.text.split(' ', 1)[1].strip() 270 | try: 271 | trigger = TextTriggerModel.get(TextTriggerModel.chat == chat, TextTriggerModel.trigger_text == trigger_text) 272 | if(trigger.locked_by): 273 | who_locked = TGUserModel.get(TGUserModel.chat_id == trigger.locked_by) 274 | bot.reply_to(msg, "You can't delete this trigger, it's locked by {}".format( 275 | '@' + who_locked.username if who_locked.username else '{}[{}]'.format( 276 | who_locked.first_name, who_locked.chat_id))) 277 | else: 278 | trigger.delete_instance() 279 | bot.reply_to(msg, "Trigger deleted.") 280 | db2dict() 281 | except DoesNotExist: 282 | bot.reply_to(msg, "Trigger not found") 283 | 284 | 285 | @bot.message_handler(func=lambda m: m.reply_to_message, commands=['del']) 286 | def del_trigger_on_reply(msg): 287 | chat = get_chat_from_message(msg) 288 | if(msg.reply_to_message.text): 289 | try: 290 | trigger = TextTriggerModel.get( 291 | TextTriggerModel.chat == chat, TextTriggerModel.response_text == msg.reply_to_message.text) 292 | if(trigger.locked_by): 293 | who_locked = TGUserModel.get(TGUserModel.chat_id == trigger.locked_by) 294 | bot.reply_to(msg, "You can't delete this trigger, it's locked by {}.".format( 295 | '@' + who_locked.username if who_locked.username else '{}[{}]'.format( 296 | who_locked.first_name, who_locked.chat_id))) 297 | else: 298 | trigger.delete_instance() 299 | bot.reply_to(msg, "Trigger deleted.") 300 | db2dict() 301 | except DoesNotExist: 302 | bot.reply_to(msg, "Trigger not found.") 303 | else: 304 | bot.reply_to(msg, "Media triggers are not supported yet.") 305 | 306 | 307 | @bot.message_handler(regexp=r'/lock .{4,3000}') 308 | def lock_trigger(msg): 309 | chat = get_chat_from_message(msg) 310 | trigger_text = msg.text.split(' ', 1)[1].strip() 311 | try: 312 | trigger = TextTriggerModel.get(TextTriggerModel.chat == chat, TextTriggerModel.trigger_text == trigger_text) 313 | if(trigger.locked_by): 314 | who_locked = TGUserModel.get(TGUserModel.chat_id == trigger.locked_by) 315 | if(trigger.locked_by == msg.from_user.id): 316 | trigger.locked_by = 0 317 | trigger.save() 318 | bot.reply_to(msg, "Trigger unlocked.") 319 | else: 320 | bot.reply_to(msg, "You can't unlock this trigger, it's locked by {}".format( 321 | '@' + who_locked.username if who_locked.username else '{}[{}]'.format( 322 | who_locked.first_name, who_locked.chat_id))) 323 | else: 324 | trigger.locked_by = msg.from_user.id 325 | trigger.save() 326 | bot.reply_to(msg, "Trigger locked.") 327 | except DoesNotExist: 328 | bot.reply_to(msg, "Trigger not found") 329 | 330 | 331 | @bot.message_handler(func=lambda m: m.reply_to_message, commands=['lock']) 332 | def lock_trigger_on_reply(msg): 333 | chat = get_chat_from_message(msg) 334 | if (msg.reply_to_message.text): 335 | try: 336 | trigger = TextTriggerModel.get( 337 | TextTriggerModel.chat == chat, TextTriggerModel.response_text == msg.reply_to_message.text) 338 | if (trigger.locked_by): 339 | who_locked = TGUserModel.get(TGUserModel.chat_id == trigger.locked_by) 340 | if (trigger.locked_by == msg.from_user.id): 341 | trigger.locked_by = 0 342 | trigger.save() 343 | bot.reply_to(msg, "Trigger unlocked.") 344 | else: 345 | bot.reply_to(msg, "You can't unlock this trigger, it's locked by {}".format( 346 | '@' + who_locked.username if who_locked.username else '{}[{}]'.format( 347 | who_locked.first_name, who_locked.chat_id))) 348 | else: 349 | trigger.locked_by = msg.from_user.id 350 | trigger.save() 351 | bot.reply_to(msg, "Trigger locked.") 352 | except DoesNotExist: 353 | bot.reply_to(msg, "Trigger not found.") 354 | else: 355 | bot.reply_to(msg, "Media triggers are not supported yet.") 356 | 357 | 358 | @bot.message_handler(func=lambda m: m.reply_to_message, commands=['solve']) 359 | def solve_trigger(msg): 360 | chat = get_chat_from_message(msg) 361 | if(msg.reply_to_message.text): 362 | try: 363 | trigger = TextTriggerModel.get( 364 | TextTriggerModel.chat == chat, TextTriggerModel.response_text == msg.reply_to_message.text) 365 | bot.reply_to(msg, "Trigger: \n {}".format(trigger.trigger_text)) 366 | except DoesNotExist: 367 | bot.reply_to(msg, "Trigger not found.") 368 | else: 369 | bot.reply_to(msg, "Media triggers are not supported yet.") 370 | 371 | 372 | @bot.message_handler(commands=['size']) 373 | def get_triggers_size(msg): 374 | chat = get_chat_from_message(msg) 375 | bot.reply_to(msg, "This chat has {} triggers".format(chat.chat_triggers.count())) 376 | 377 | 378 | @bot.message_handler(commands=['all']) 379 | def get_triggers_list(msg): 380 | chat = get_chat_from_message(msg) 381 | if(len(chat.chat_triggers) > 0): 382 | trigger_listing = "Triggers:\n{}".format(','.join([trigger.trigger_text for trigger in chat.chat_triggers])) 383 | if(len(trigger_listing) > 3000): 384 | with tempfile.NamedTemporaryFile('w') as tmpfile: 385 | tmpfile.write(trigger_listing) 386 | bot.send_document(msg.chat.id, tmpfile, msg.message_id) 387 | else: 388 | bot.reply_to(msg, trigger_listing) 389 | else: 390 | bot.reply_to(msg, "This chat doesn't have triggers.") 391 | 392 | 393 | @bot.message_handler(commands=['about']) 394 | def send_about_message(msg): 395 | about_message = ''' 396 | TriggerBot *%s* (SQLite version) 397 | [Source Code on Github](https://github.com/sanguchi/TriggerBot/) 398 | [Give me 5 Stars](https://telegram.me/storebot?start=TriggerResponseBot) 399 | ''' % __version__ 400 | bot.reply_to(msg, about_message, parse_mode="Markdown") 401 | 402 | 403 | @bot.message_handler(commands=['help', 'start']) 404 | def send_help_message(msg): 405 | bot.reply_to(msg, full_help_message if msg.chat.id == msg.from_user.id else help_message, parse_mode="Markdown") 406 | 407 | 408 | @bot.message_handler(commands=['source']) 409 | def send_source_code(msg): 410 | bot.send_document(msg.chat.id, open(__file__, 'rb'), msg.message_id, "Libraries needed:\npyTelegramBotAPI, peewee") 411 | 412 | 413 | @bot.message_handler(commands=['broadcast'], func=lambda m: m.from_user.id == owner) 414 | def admin_broadcast(msg): 415 | if(msg.text.count(' ') > 0): 416 | broadcast_text = msg.text.split(' ', 1)[1] 417 | for user in TGUserModel.select(): 418 | bot.send_message(user.chat_id, broadcast_text) 419 | bot.send_message(msg.chat.id, "Broadcast sent.") 420 | else: 421 | bot.send_message(msg.chat.id, "No text provided, usage:\n-->`/broadcast `", parse_mode="Markdown") 422 | 423 | 424 | @bot.message_handler(commands=['database'], func=lambda m: m.from_user.id == owner) 425 | def admin_send_database(msg): 426 | bot.send_document(msg.chat.id, open('{}.db'.format(__file__), 'rb')) 427 | 428 | 429 | # TODO: admin commands: clean, stats. 430 | # TODO: inline triggers, media. 431 | # TODO: main loop that listen to messages to trigger. 432 | # TODO: fix database mode. 433 | 434 | # Catch every message, for triggers. 435 | @bot.message_handler(content_types=['text']) 436 | def catch_messages(msg): 437 | if(triggers_dict): 438 | # print("Using dict") 439 | trigger_list = triggers_dict[str(msg.chat.id)] 440 | # print("Trigger list: ", trigger_list) 441 | for trigger_text in trigger_list.keys(): 442 | # print("Checking against: ", trigger_text) 443 | if(trigger_text in msg.text.lower()): 444 | bot.reply_to(msg, trigger_list[trigger_text]) 445 | else: 446 | # print("Using database") 447 | try: 448 | chat = TGUserModel.get(TGUserModel.chat_id == msg.chat.id) 449 | except DoesNotExist: 450 | chat = TGUserModel.create(chat_id=msg.chat.id, first_name=msg.chat.first_name, username=msg.chat.username) 451 | chat.save() 452 | # print("Trigger count for", chat.chat_id, ": ", chat.chat_triggers.count()) 453 | for trigger_model in chat.chat_triggers: 454 | # print("Checking against: ", trigger_model.trigger_text) 455 | if(trigger_model.trigger_text in msg.text.lower()): 456 | bot.reply_to(msg, trigger_model.response_text) 457 | 458 | # print("Starting") 459 | # bot.polling() 460 | 461 | 462 | # This makes the bot unstoppable :^) 463 | def safepolling(bot_instance): 464 | if(bot_instance.skip_pending): 465 | lid = bot_instance.get_updates()[-1].update_id 466 | else: 467 | lid = 0 468 | while(1): 469 | try: 470 | updates = bot_instance.get_updates(lid + 1, 50) 471 | # print('len updates = %s' % len(updates)) 472 | if(len(updates) > 0): 473 | lid = updates[-1].update_id 474 | bot_instance.process_new_updates(updates) 475 | except ApiException as a: 476 | print(a) 477 | except Exception as e: 478 | print('Exception at %s \n%s' % (asctime(), e)) 479 | now = int(time()) 480 | while(1): 481 | error_text = 'Exception at %s:\n%s' % (asctime(), str(e) if len(str(e)) < 3600 else str(e)[:3600]) 482 | try: 483 | # print('Trying to send message to owner.') 484 | offline = int(time()) - now 485 | bot_instance.send_message(owner, error_text + '\nBot went offline for %s seconds' % offline) 486 | # print('Message sent, returning to polling.') 487 | break 488 | except: 489 | sleep(0.25) 490 | 491 | 492 | if(__name__ == '__main__'): 493 | # Bot starts here. 494 | 495 | print('Bot started.') 496 | try: 497 | print('Bot username:[%s]' % bot.get_me().username) 498 | except ApiException: 499 | print('The given token [%s] is invalid, please fix it') 500 | exit(1) 501 | # Tell owner the bot has started. 502 | try: 503 | bot.send_message(owner, 'Bot Started') 504 | except ApiException: 505 | print('''Make sure you have started your bot https://telegram.me/%s. 506 | And configured the owner variable.''' % bot.get_me().username) 507 | exit(1) 508 | print('Safepolling Start.') 509 | safepolling(bot) 510 | 511 | # Nothing beyond this line will be executed. 512 | -------------------------------------------------------------------------------- /TriggerBotTornado.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import telebot 3 | import json 4 | import requests 5 | import tornado.escape 6 | import tornado.ioloop 7 | import tornado.web 8 | import logging 9 | from tornado.options import define, options, parse_command_line 10 | from time import time 11 | from os.path import exists 12 | from telebot.apihelper import ApiException 13 | 14 | __version__ = 0.1 15 | logger = telebot.logger 16 | telebot.logger.setLevel(logging.INFO) 17 | define("port", default=8888, help="run on the given port", type=int) 18 | 19 | # Git Repo: 20 | # https://github.com/sanguchi/TriggerBot 21 | 22 | # GLOBAL VARIABLES. 23 | 24 | # Bot owner, replace with your user_id. 25 | owner = 59802458 26 | 27 | # Variable to hold all Triggers. 28 | triggers = {} 29 | 30 | # Separator character. 31 | separator = '/' 32 | 33 | # Webhook domain, example: https://www.mydomain.com/. 34 | webhook_domain = None 35 | 36 | # if you won't use ngrok specify your server url/ip above 37 | if(not webhook_domain): 38 | try: 39 | # if ngrok is running, ask for the url. 40 | webhook_domain = requests.get("http://localhost:4040/api/tunnels").json()['tunnels'][0]['public_url'] 41 | logger.info("Webhook domain: {}".format(webhook_domain)) 42 | 43 | except Exception: 44 | logger.error("Webhook domain not set.") 45 | exit() 46 | 47 | 48 | # Check if a message is too old. 49 | def is_recent(m): 50 | return (time() - m.date) < 60 51 | 52 | # END OF GLOBAL VARIABLES SECTION. 53 | 54 | # TRIGGERS SECTION 55 | 56 | # Check if Triggers file exists and load, if not, is created. 57 | if exists('triggers.json'): 58 | with open('triggers.json') as f: 59 | triggers = json.load(f) 60 | logger.info('Triggers file loaded.') 61 | else: 62 | with open('triggers.json', 'w') as f: 63 | json.dump({}, f) 64 | 65 | 66 | # Function to save triggers list to a file. 67 | def save_triggers(): 68 | with open('triggers.json', 'w') as f: 69 | json.dump(triggers, f, indent=2) 70 | logger.info('Triggers file saved.') 71 | 72 | 73 | # Function to get triggers list for a group. 74 | def get_triggers(group_id): 75 | if(str(group_id) in triggers.keys()): 76 | return triggers[str(group_id)] 77 | else: 78 | return False 79 | 80 | # END OF TRIGGERS SECTION 81 | 82 | # BOT INIT SECTION. 83 | token = '' 84 | 85 | # Check if Token file exists, if not, create. 86 | if exists('token.txt'): 87 | with open('token.txt') as f: 88 | token = f.readline().strip() 89 | logger.info('Token Loaded') 90 | else: 91 | no_token_message = 'No token file detected, please paste or type here your token:\n> ' 92 | token = input(no_token_message) 93 | with open('token.txt', 'w') as f: 94 | f.write(token) 95 | logger.info('Token File saved.') 96 | 97 | # Create Bot. 98 | bot = telebot.TeleBot(token) 99 | # Bot user ID. 100 | bot_id = int(token.split(':')[0]) 101 | print('Bot ID [%s]' % bot_id) 102 | 103 | 104 | # Define a custom Listener to print messages to console. 105 | # Python2 version 106 | def listener2(messages): 107 | for m in messages: 108 | cid = m.chat.id 109 | name = m.from_user.first_name.encode('ascii', 'ignore').decode('ascii') 110 | if(m.content_type == 'text'): 111 | message_text = m.text.encode('ascii', 'ignore').decode('ascii') 112 | else: 113 | message_text = m.content_type 114 | print('{}[{}]:{}'.format(name, cid, message_text)) 115 | 116 | 117 | # Python3 version. 118 | def listener3(messages): 119 | for m in messages: 120 | print('{}[{}]:{}'.format( 121 | m.from_user.first_name, 122 | m.chat.id, 123 | m.text if m.text else m.content_type 124 | )) 125 | 126 | # Change to listener2 if this complains about encoding. 127 | bot.set_update_listener(listener3) 128 | 129 | # END OF BOT INITIALIZATION SECTION. 130 | 131 | # GLOBAL MESSAGES SECTION. 132 | about_message = ''' 133 | TriggerBot *%s* (Webhook) 134 | [Source Code on Github](https://github.com/sanguchi/TriggerBot/) 135 | [Give me 5 Stars](https://telegram.me/storebot?start=TriggerResponseBot) 136 | ''' % __version__ 137 | 138 | help_message = ''' 139 | You need help! 140 | *Commands:* 141 | `/add / ` 142 | |-_Adds a new trigger._ 143 | `/del ` 144 | |-_deletes trigger if exists._ 145 | *More:* 146 | /about - /size - /all - /source 147 | *For a detailed help send /help in private.* 148 | ''' 149 | 150 | full_help = ''' 151 | You really need help! 152 | *Main Functions:* 153 | *Add Triggers:* There are two ways. 154 | 1)`/add / ` 155 | Example: 156 | */add Hello / Hello there!* 157 | Also works via reply. 158 | _In Reply to Another Message:_ 159 | 2)`/add ` 160 | *Delete Triggers:* 161 | `/del ` 162 | Deletes a defined trigger, example: 163 | `/del hello` 164 | *Misc:* 165 | /size 166 | _Returns size of triggers list._ 167 | /all 168 | _List all triggers._ 169 | /help 170 | _This message._ 171 | /source 172 | _Sends source code TriggerBot.py_ 173 | /solve 174 | _Resolve what trigger causes the given response, if exists._ 175 | *Also works by reply:* 176 | _Reply to any bot response with the command to get the trigger._ 177 | /about 178 | _About this bot._ 179 | ''' 180 | 181 | added_message = ''' 182 | New Trigger Created: 183 | Trigger [{}] 184 | Response [{}] 185 | ''' 186 | 187 | gadded_message = ''' 188 | New Global Trigger Created: 189 | Trigger [{}] 190 | Response [{}] 191 | ''' 192 | 193 | invited_message = ''' 194 | Okay, Hi everyone, i'm *Trigger*, a bot made to store sentences as triggers. 195 | And these words will trigger a defined response. 196 | By default, hi have 4 triggers defined. 197 | Type `tutorial` to see. 198 | _Be nice._ 199 | ''' 200 | 201 | gdeleted_message = ''' 202 | Trigger [{}] deleted from {} Groups. 203 | ''' 204 | tutorial = ''' 205 | Reply to this message with '/solve' to know what word triggers this message. 206 | Reply to this message with '/del' to delete this tutorial message. 207 | Reply to this message with '/add something' to set the word something as a trigger for this message. 208 | Write /all to see all defined triggers. 209 | Write /size to see how many triggers are defined. 210 | Send a message with your chat rules, and then reply to that message with: 211 | /add #rules 212 | To save them in a trigger. 213 | ''' 214 | default_triggers = { 215 | 'trigger': 'Are you triggered?', 216 | 'oh shit': 'TRIGGERED!', 217 | 'tutorial': tutorial, 218 | 'fuck': 'Watch your language!'} 219 | 220 | # END OF GLOBAL MESSAGES SECTION. 221 | 222 | # COMMAND IMPLEMENTATION SECTION. 223 | 224 | 225 | @bot.message_handler(commands=['add']) 226 | def add_trigger(m): 227 | if(m.reply_to_message): 228 | if(m.reply_to_message.text): 229 | if(len(m.reply_to_message.text.split()) < 2): 230 | bot.reply_to(m, 'Bad Arguments') 231 | return 232 | trigger_word = m.text.split(' ', 1)[1].strip().lower() 233 | trigger_response = m.reply_to_message.text.strip() 234 | else: 235 | bot.reply_to(m, 'Only text triggers are supported.') 236 | return 237 | else: 238 | if(len(m.text.split()) < 2): 239 | bot.reply_to(m, 'Bad Arguments') 240 | return 241 | if(m.text.find(separator, 1) == -1): 242 | bot.reply_to(m, 'Separator not found') 243 | return 244 | rest_text = m.text.split(' ', 1)[1] 245 | trigger_word = rest_text.split(separator)[0].strip().lower() 246 | trigger_response = rest_text.split(separator, 1)[1].strip() 247 | 248 | if(len(trigger_word) < 4): 249 | bot.reply_to(m, 'Trigger too short. [chars < 4]') 250 | return 251 | if(len(trigger_response) < 1): 252 | bot.reply_to(m, 'Invalid Response.') 253 | return 254 | if(len(trigger_response) > 3000): 255 | bot.reply_to(m, 'Response too long. [chars > 3000]') 256 | return 257 | if(m.chat.type in ['group', 'supergroup']): 258 | if(get_triggers(m.chat.id)): 259 | get_triggers(m.chat.id)[trigger_word] = trigger_response 260 | else: 261 | triggers[str(m.chat.id)] = {trigger_word: trigger_response} 262 | msg = added_message.format(trigger_word, trigger_response) 263 | bot.reply_to(m, msg) 264 | save_triggers() 265 | else: 266 | if(m.chat.id != owner): 267 | return 268 | 269 | 270 | @bot.message_handler(commands=['del']) 271 | def delete_trigger(m): 272 | # /del on reply handling. 273 | if(len(m.text.split()) == 1 and m.reply_to_message and m.reply_to_message.text): 274 | trg = get_triggers(m.chat.id) 275 | if(trg): 276 | for x in trg.keys(): 277 | if(trg[x].lower() == m.reply_to_message.text.lower()): 278 | trg.pop(x) 279 | bot.reply_to(m, 'Trigger [{}] deleted.'.format(x)) 280 | return 281 | if(len(m.text.split()) < 2): 282 | bot.reply_to(m, 'Bad Arguments') 283 | return 284 | del_text = m.text.split(' ', 1)[1].strip() 285 | if(m.chat.type in ['group', 'supergroup']): 286 | trg = get_triggers(m.chat.id) 287 | if(trg and del_text in trg.keys()): 288 | trg.pop(del_text) 289 | bot.reply_to(m, 'Trigger [{}] deleted.'.format(del_text)) 290 | save_triggers() 291 | else: 292 | bot.reply_to(m, 'Trigger [{}] not found.'.format(del_text)) 293 | 294 | 295 | @bot.message_handler(commands=['size']) 296 | def list_size(m): 297 | if(m.chat.type in ['group', 'supergroup']): 298 | trg = get_triggers(m.chat.id) 299 | if(trg): 300 | msg = 'Size of Triggers List = {}'.format(len(trg)) 301 | bot.reply_to(m, msg) 302 | else: 303 | bot.reply_to(m, 'Size of Triggers List = 0') 304 | 305 | 306 | @bot.message_handler(commands=['all']) 307 | def list_all_triggers(m): 308 | if(m.chat.type in ['group', 'supergroup']): 309 | trg = get_triggers(m.chat.id) 310 | if(trg): 311 | if(len(trg.keys()) == 0): 312 | bot.reply_to(m, 'This group doesn\'t have triggers.') 313 | else: 314 | bot.reply_to(m, 'Triggers:\n' + '\n'.join(trg)) 315 | else: 316 | bot.reply_to(m, 'This group doesn\'t have triggers.') 317 | 318 | 319 | @bot.message_handler(commands=['help', 'start']) 320 | def send_help_message(m): 321 | if(m.chat.id == m.from_user.id): 322 | bot.send_message(m.chat.id, full_help, True, parse_mode="Markdown") 323 | else: 324 | bot.send_message(m.chat.id, help_message, True, parse_mode="Markdown") 325 | 326 | 327 | @bot.message_handler(commands=['source']) 328 | def send_source_file(m): 329 | if exists(__file__): 330 | bot.send_document(m.chat.id, open(__file__,'rb')) 331 | else: 332 | bot.reply_to(m, "No source file found :x") 333 | 334 | 335 | @bot.message_handler(commands=['solve']) 336 | def solve_trigger(m): 337 | rp = m.reply_to_message 338 | rw = '' 339 | ts = 'Trigger not Found.' 340 | if(len(m.text.split()) >= 2): 341 | rw = m.text.split(' ', 1)[1] 342 | if(rp and rp.from_user.id == bot_id and rp.text): 343 | rw = rp.text 344 | if(m.chat.type in ['group', 'supergroup']): 345 | trg = get_triggers(m.chat.id) 346 | if(trg): 347 | for x in trg.keys(): 348 | if(trg[x].lower() == rw.lower()): 349 | ts = 'Trigger = ' + x 350 | bot.reply_to(m, ts) 351 | 352 | 353 | @bot.message_handler(commands=['about']) 354 | def send_about_message(m): 355 | bot.reply_to(m, about_message, parse_mode="Markdown") 356 | 357 | 358 | # END OF COMMAND IMPLEMENTATION SECTION. 359 | 360 | # ADMIN COMMANDS 361 | @bot.message_handler(commands=['broadcast']) 362 | def send_broadcast(m): 363 | if(m.from_user.id != owner): 364 | return 365 | if(len(m.text.split()) == 1): 366 | bot.send_message(m.chat.id, 'No text provided!') 367 | return 368 | count = 0 369 | for g in triggers.keys(): 370 | try: 371 | bot.send_message(int(g), m.text.split(' ', 1)[1]) 372 | count += 1 373 | except: 374 | continue 375 | bot.send_message(m.chat.id, 'Broadcast sent to {} groups of {}'.format(count, len(triggers))) 376 | 377 | 378 | @bot.message_handler(commands=['triggers']) 379 | def send_triggers(m): 380 | if(m.from_user.id == owner): 381 | bot.send_document(owner, open('triggers.json')) 382 | 383 | 384 | @bot.message_handler(commands=['gadd']) 385 | def add_global_trigger(m): 386 | if(m.from_user.id == owner): 387 | if(len(m.text.split()) == 1): 388 | bot.reply_to(m, 'Usage:\n/gadd / \n[In reply]:\n/gadd ') 389 | return 390 | if(m.reply_to_message): 391 | if(m.reply_to_message.text): 392 | trigger_response = m.reply_to_message.text 393 | trigger_word = m.text 394 | else: 395 | if(m.text.find(separator, 1) == -1): 396 | bot.reply_to(m, 'Separator not found') 397 | return 398 | rest_text = m.text.split(' ', 1)[1] 399 | trigger_word = rest_text.split(separator)[0].strip().lower() 400 | trigger_response = rest_text.split(separator, 1)[1].strip() 401 | if(len(trigger_word) < 4): 402 | bot.reply_to(m, 'Trigger too short. [chars < 4]') 403 | return 404 | if(len(trigger_response) < 1): 405 | bot.reply_to(m, 'Invalid Response.') 406 | return 407 | for g in triggers.keys(): 408 | triggers[g][trigger_word] = trigger_response 409 | bot.reply_to(m, gadded_message.format(trigger_word, trigger_response)) 410 | save_triggers() 411 | 412 | 413 | @bot.message_handler(commands=['gdel']) 414 | def global_delete(m): 415 | if(m.from_user.id == owner): 416 | if(len(m.text.split()) == 1): 417 | bot.reply_to(m, 'Usage: /gdel ') 418 | else: 419 | trigger_word = m.text.split(' ', 1)[1] 420 | count = 0 421 | for g in triggers.keys(): 422 | if(trigger_word in triggers[g]): 423 | triggers[g].pop(trigger_word) 424 | count += 1 425 | bot.reply_to(m, gdeleted_message.format(trigger_word, count)) 426 | save_triggers() 427 | 428 | 429 | @bot.message_handler(commands=['gsearch']) 430 | def global_search(m): 431 | if(m.from_user.id == owner): 432 | if(len(m.text.split()) == 1): 433 | bot.reply_to(m, 'Usage: /gsearch ') 434 | else: 435 | trigger_word = m.text.split(' ', 1)[1] 436 | results = [] 437 | for g in triggers.keys(): 438 | if(trigger_word in triggers[g].keys()): 439 | txt = triggers[g][trigger_word] 440 | results.append('[%s]:\n%s' % (g, txt if len(txt) < 30 else txt[:27] + '...')) 441 | if(len(results) == 0): 442 | result_text = 'Trigger not found' 443 | else: 444 | result_text = 'Trigger found in these groups:\n%s' % '\n-----\n'.join(results) 445 | bot.reply_to(m, result_text) 446 | 447 | 448 | @bot.message_handler(commands=['stats']) 449 | def bot_stats(m): 450 | if(m.from_user.id == owner): 451 | total_triggers = 0 452 | for x in triggers.keys(): 453 | total_triggers += len(triggers[x].keys()) 454 | stats_text = 'Chats : {}\nTriggers : {}'.format(len(triggers), total_triggers) 455 | bot.reply_to(m, stats_text) 456 | 457 | 458 | @bot.message_handler(commands=['clean']) 459 | def clean_triggers(m): 460 | if(m.from_user.id == owner): 461 | group_count = len(triggers) 462 | 463 | total_triggers = 0 464 | for x in triggers.keys(): 465 | total_triggers += len(triggers[x].keys()) 466 | 467 | triggers_count = 0 468 | for g in triggers.copy().keys(): 469 | try: 470 | bot.send_chat_action(g, 'typing') 471 | except: 472 | triggers_count += len(triggers.pop(g)) 473 | 474 | msg_text = ''' 475 | _Original group count_ : *{}* 476 | _Original trigger count_ : *{}* 477 | _Groups deleted_ : *{}* 478 | _Triggers deleted_ : *{}* 479 | _Final group count_ : *{}* 480 | _Final trigger count_ : *{}* 481 | '''.format( 482 | group_count, 483 | total_triggers, 484 | group_count - len(triggers), 485 | triggers_count, 486 | len(triggers), 487 | total_triggers - triggers_count) 488 | save_triggers() 489 | bot.send_message(m.chat.id, msg_text, parse_mode="Markdown") 490 | 491 | 492 | @bot.message_handler(commands=['merge']) 493 | def merge_triggers(m): 494 | if(m.from_user.id == owner): 495 | success_text = 'Triggers merged with [{}], total triggers: [{}]' 496 | no_exists = 'Group {} does not exist in the database.' 497 | if(len(m.text.split()) == 2): 498 | merge_from = int(m.text.split()[1]) 499 | if(get_triggers(merge_from)): 500 | get_triggers(m.chat.id).update(get_triggers(merge_from)) 501 | save_triggers() 502 | bot.reply_to(m, success_text.format(merge_from, len(get_triggers(m.chat.id)))) 503 | else: 504 | bot.reply_to(m, no_exists.format(merge_from)) 505 | else: 506 | bot.reply_to(m, 'Missing argument, Group id.') 507 | 508 | 509 | @bot.message_handler(commands=['check_groups']) 510 | def check_groups(m): 511 | if(m.from_user.id == owner): 512 | group_count = 0 513 | for g in triggers.keys(): 514 | try: 515 | bot.send_chat_action(g, 'typing') 516 | group_count += 1 517 | except: 518 | pass 519 | bot.send_message(m.chat.id, 'Working in %s of %s chats' % (group_count, len(triggers))) 520 | 521 | 522 | # Triggered when you add the bot to a new group. 523 | @bot.message_handler(content_types=['new_chat_member']) 524 | def bot_joined(m): 525 | if(m.new_chat_member.id == bot_id): 526 | bot.send_message(m.chat.id, invited_message, parse_mode="Markdown") 527 | if(not get_triggers(m.chat.id)): 528 | triggers[str(m.chat.id)] = default_triggers 529 | save_triggers() 530 | bot.send_message(owner, 'Bot added to %s[%s]' % (m.chat.title, m.chat.id)) 531 | 532 | 533 | @bot.message_handler(content_types=['left_chat_member']) 534 | def bot_left(m): 535 | if(m.left_chat_member.id == bot_id): 536 | bot.send_message(owner, 'Bot left chat %s[%s]' % (m.chat.title, m.chat.id)) 537 | 538 | 539 | # TRIGGER PROCESSING SECTION. 540 | # Catch every message, for triggers. 541 | @bot.message_handler(func=lambda m: True) 542 | def response(m): 543 | if(not is_recent(m)): 544 | return 545 | if(m.chat.type in ['group', 'supergroup']): 546 | trg = get_triggers(m.chat.id) 547 | if(trg): 548 | for t in trg.keys(): 549 | if t in m.text.lower(): 550 | bot.reply_to(m, trg[t]) 551 | 552 | 553 | class WebhookHandler(tornado.web.RequestHandler): 554 | 555 | def check_xsrf_cookie(self): 556 | pass 557 | 558 | def post(self): 559 | self.set_status(200) 560 | json_string = self.request.body.decode('utf-8') 561 | update = telebot.types.Update.de_json(json_string) 562 | bot.process_new_messages([update.message]) 563 | 564 | 565 | class AdminHandler(tornado.web.RequestHandler): 566 | 567 | def get(self, *args, **kwargs): 568 | pass 569 | 570 | def post(self): 571 | pass 572 | 573 | 574 | def main(): 575 | # Bot starts here. 576 | print('Bot started.') 577 | try: 578 | print('Bot username:[{}]'.format(bot.get_me().username)) 579 | except ApiException: 580 | print('The given token [{}] is invalid, please fix it'.format(token)) 581 | exit(1) 582 | # Tell owner the bot has started. 583 | try: 584 | bot.send_message(owner, 'Bot Started') 585 | except ApiException: 586 | print('''Make sure you have started your bot https://telegram.me/{} 587 | And configured the owner variable.'''.format(bot.get_me().username)) 588 | exit(1) 589 | 590 | # Tornado server initialization. 591 | print('Tornado server starting.') 592 | parse_command_line() 593 | app = tornado.web.Application( 594 | [ 595 | (r"/{}".format(token), WebhookHandler), 596 | (r"/{}/admin".format(token), AdminHandler), 597 | ], 598 | cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", 599 | xsrf_cookies=True, 600 | ) 601 | 602 | # Reset webhook. 603 | bot.remove_webhook() 604 | webhook_url = '{}/{}'.format(webhook_domain, token) 605 | bot.set_webhook(webhook_url) 606 | app.listen(options.port) 607 | tornado.ioloop.IOLoop.current().start() 608 | 609 | if __name__ == "__main__": 610 | main() 611 | -------------------------------------------------------------------------------- /TriggerBot_Old.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import telebot 3 | import json 4 | from time import time 5 | from os.path import exists 6 | 7 | # comment to use default timeout. (3.5) 8 | telebot.apihelper.CONNECT_TIMEOUT = 9999 9 | 10 | triggers = {} 11 | tfile = "triggers.json" 12 | tokenf = "token.txt" 13 | ignored = [] 14 | separator = '/' 15 | 16 | admins = [59802458] 17 | 18 | 19 | def is_recent(m): 20 | return (time() - m.date) < 60 21 | 22 | 23 | # Check if Triggers file exists. 24 | if exists(tfile): 25 | with open(tfile) as f: 26 | triggers = json.load(f) 27 | else: 28 | # print("Triggers file not found, creating.") 29 | with open(tfile,'w') as f: 30 | json.dump({}, f) 31 | 32 | # Check if Token file exists, if not, create. 33 | if exists(tokenf): 34 | with open(tokenf) as f: 35 | token = f.readline().rstrip('\n') 36 | # print("Token = [" + token + "]") 37 | else: 38 | # print("Token File not found, creating.") 39 | with open(tokenf, 'w') as f: 40 | try: 41 | token = raw_input('Token not found, please paste/write your token.\n> ') 42 | except NameError: 43 | token = input('Token not found, please paste/write your token.\n> ') 44 | f.write(token) 45 | 46 | 47 | # Function to add new Trigger - Response 48 | def newTrigger(trigger, response): 49 | triggers[trigger.lower()] = response 50 | with open(tfile, "w") as f: 51 | json.dump(triggers, f, indent=1) 52 | # print("triggers file saved") 53 | 54 | 55 | # Create Bot. 56 | bot = telebot.TeleBot(token) 57 | 58 | 59 | # Define a custom Listener to print messages to console. 60 | 61 | # Python2 version 62 | def listener2(messages): 63 | for m in messages: 64 | cid = m.chat.id 65 | name = m.from_user.first_name.encode('ascii', 'ignore').decode('ascii') 66 | if(m.content_type == 'text'): 67 | message_text = m.text.encode('ascii', 'ignore').decode('ascii') 68 | else: 69 | message_text = m.content_type 70 | print('{}[{}]:{}'.format(name, cid, message_text)) 71 | 72 | 73 | # Python3 version. 74 | def listener3(messages): 75 | print('\n'.join( 76 | ['%s[%s]:%s'.format( 77 | message.from_user.first_name, message.chat.id, message.text if message.text else message.content_type) 78 | for message in messages])) 79 | 80 | 81 | # Change to listener if this complains about encoding. 82 | bot.set_update_listener(listener3) 83 | 84 | 85 | help_text = ''' 86 | Main commands: 87 | /add / 88 | /del 89 | Other Commands: 90 | /about 91 | /source 92 | /all 93 | ''' 94 | 95 | about_text = ''' 96 | Created by @Sanguchi in 60 minutes :P 97 | [Source Code on Github](https://github.com/sanguchi/TriggerBot/) 98 | ''' 99 | 100 | 101 | # Adds another trigger-response. ex: "/add Hi / Hi!! :DD" 102 | @bot.message_handler(commands=['add']) 103 | def add_trigger(m): 104 | if(len(m.text.split()) > 1): 105 | rest = m.text.split(' ', 1)[1] 106 | if(m.text.find('/') != -1): 107 | trigger, response = [x.strip() for x in rest.split('/', 1)] 108 | if(len(trigger) >= 4): 109 | newTrigger(trigger, response) 110 | bot.reply_to(m, "Trigger Added: Trigger[%s] - Response[%s]" % (trigger, response)) 111 | else: 112 | bot.reply_to(m, "Trigger too short. [chars < 4]") 113 | else: 114 | bot.reply_to(m, 'Separator not found.') 115 | else: 116 | bot.reply_to(m, 'Usage: /add / ') 117 | 118 | 119 | @bot.message_handler(commands=['del']) 120 | def delete_trigger(m): 121 | if(len(m.text.split()) > 1): 122 | if(m.text.split()[1] in triggers.keys()): 123 | triggers.pop(m.text.split()[1]) 124 | bot.reply_to(m, 'Trigger [%s] deleted.' % m.text.split()[1]) 125 | with open(tfile, "w") as the_file: 126 | json.dump(triggers, the_file) 127 | print("triggers file saved") 128 | else: 129 | bot.reply_to(m, 'Trigger not found.') 130 | else: 131 | bot.reply_to(m, 'Usage: /del ') 132 | 133 | 134 | # Answers with the size of triggers. 135 | @bot.message_handler(commands=['size']) 136 | def trigger_list_size(m): 137 | bot.reply_to(m, 'Size of triggers list: %s' % len(triggers)) 138 | 139 | 140 | # Help message. 141 | @bot.message_handler(commands=['help']) 142 | def send_help(m): 143 | bot.reply_to(m, help_text) 144 | 145 | 146 | # About message. 147 | @bot.message_handler(commands=['about']) 148 | def send_about(m): 149 | bot.reply_to(m, about_text, parse_mode="Markdown") 150 | 151 | 152 | # Sends source file (THIS FILE) 153 | @bot.message_handler(commands=['source']) 154 | def source_code(m): 155 | cid = m.chat.id 156 | if exists(__file__): 157 | bot.send_document(cid, open(__file__,'rb')) 158 | else: 159 | bot.reply_to(m, "No source file found :x") 160 | 161 | 162 | @bot.message_handler(commands=['all']) 163 | def all_triggers(m): 164 | bot.reply_to(m, 'Triggers:\n%s' % '\n'.join(triggers.keys())) 165 | 166 | 167 | @bot.message_handler(commands=['triggers']) 168 | def send_triggers(m): 169 | if(m.from_user.id in admins): 170 | bot.send_document(m.from_user.id, open(tfile), m.message_id, None, None) 171 | 172 | 173 | # Catch every message, for triggers :D 174 | @bot.message_handler(func=lambda m: True) 175 | def response(m): 176 | # print("Checking for triggers in Message [" + m.text + "]") 177 | for t in triggers.keys(): 178 | if t in m.text: 179 | bot.reply_to(m, triggers[t]) 180 | 181 | 182 | # This makes the bot unstoppable :^) 183 | def safepolling(bot_obj): 184 | now = int(time()) 185 | while(1): 186 | try: 187 | print('Bot went offline for {} seconds.'.format(int(time()) - now)) 188 | bot_obj.polling() 189 | except Exception as e: 190 | bot_obj.stop_polling() 191 | now = int(time()) 192 | error_text = 'Something went wrong:\n%s' % str(e) if len(str(e)) < 3600 else str(e)[:3600] 193 | for x in admins: 194 | while(1): 195 | try: 196 | offline = int(time()) - now 197 | bot_obj.send_message(x, error_text + '\nBot went offline for %s seconds' % offline) 198 | break 199 | except: 200 | pass 201 | 202 | # Bot starts here. 203 | print('Bot started.') 204 | print('Bot username:[%s]' % bot.get_me().username) 205 | [bot.send_message(x, "Bot started") for x in admins] 206 | print('Safepolling Start.') 207 | safepolling(bot) 208 | # Nothing beyond this line will be executed. 209 | -------------------------------------------------------------------------------- /example_dot_env: -------------------------------------------------------------------------------- 1 | # Copy this file, rename it to .env and fill/change the values. 2 | # Example token: 3 | # BOT_TOKEN=157963248:AwwecwWEgTGD85wewert23fgttytWDEgT5R 4 | BOT_TOKEN= 5 | # Example ID: 6 | # OWNER_ID=9284284 7 | OWNER_ID= 8 | # Change to True or False 9 | DEBUG=False 10 | # POSTGRESQL Credentials 11 | PG_USER= 12 | PG_PASS= 13 | # Postgres Database name 14 | PG_DTBS= 15 | PG_HOST= -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2017.11.5 2 | chardet==3.0.4 3 | idna==2.6 4 | markovify==0.7.1 5 | peewee==3.13.1 6 | psycopg2==2.7.3.2 7 | pyTelegramBotAPI==3.5.1 8 | python-decouple==3.1 9 | requests==2.20.1 10 | six==1.11.0 11 | tornado==4.5.3 12 | Unidecode==1.0.22 13 | urllib3==1.24.2 14 | --------------------------------------------------------------------------------