forked from dymik739/modular-bot-framework-for-telegram
		
	Initial commit
This commit is contained in:
		
						commit
						84d6b3d58f
					
				
							
								
								
									
										200
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,200 @@
 | 
				
			|||||||
 | 
					from telegram.ext import Updater, MessageHandler, Filters
 | 
				
			||||||
 | 
					import codecs
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import threading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# global variables
 | 
				
			||||||
 | 
					STOP_REQUESTED = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# some functions that increase readability of the code
 | 
				
			||||||
 | 
					def readfile(filename):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        return codecs.open(filename, encoding = "utf-8").read()
 | 
				
			||||||
 | 
					    except FileNotFoundError:
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        print( "[ERROR] Unexpected error occured in readfile() ({0})".format(e) )
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# module object classes
 | 
				
			||||||
 | 
					class ModuleV1:
 | 
				
			||||||
 | 
					    def __init__(self, path, code, enabled, alias):
 | 
				
			||||||
 | 
					        self.version = 1
 | 
				
			||||||
 | 
					        self.enabled = enabled
 | 
				
			||||||
 | 
					        self.code = code
 | 
				
			||||||
 | 
					        self.alias = alias
 | 
				
			||||||
 | 
					        self.path = path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # set environmental variables
 | 
				
			||||||
 | 
					    def set_env(self):
 | 
				
			||||||
 | 
					        self.RESPONCE = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # running the module
 | 
				
			||||||
 | 
					    def process(self, msg):
 | 
				
			||||||
 | 
					        self.set_env()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.MESSAGE = msg
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            exec(self.code)
 | 
				
			||||||
 | 
					            return self.RESPONCE
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            print("[ERROR] module: module \"{}\" ({}) raised exception \"{}\"".format(self.path, self.alias, e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# module control unit
 | 
				
			||||||
 | 
					class ModuleControlUnit:
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.modules = []
 | 
				
			||||||
 | 
					        self.reload_modules()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        print("[INFO] ModuleControlUnit: initialized successfully")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reload_modules(self):
 | 
				
			||||||
 | 
					        for folder in os.listdir("modules/"):
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                meta_raw = readfile("modules/{}/meta.json".format(folder))
 | 
				
			||||||
 | 
					                if not meta_raw:
 | 
				
			||||||
 | 
					                    print("[WARN] module_loader: no meta.json found in module folder \"{}\"".format(folder))
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                meta = json.loads( meta_raw )
 | 
				
			||||||
 | 
					                if "version" in meta:
 | 
				
			||||||
 | 
					                    if meta["version"] == 1:
 | 
				
			||||||
 | 
					                        if "index_file" in meta:
 | 
				
			||||||
 | 
					                            index_file = meta["index_file"]
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            index_file = "index.py"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        code = readfile( "modules/{}/{}".format(folder, index_file) )
 | 
				
			||||||
 | 
					                        if not code: # False both when readfile() returns False and when the code string is empty
 | 
				
			||||||
 | 
					                            print("[WARN] reload_modules: module {} does not have any code, skipping...".format(folder))
 | 
				
			||||||
 | 
					                            continue
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        if "start_on_boot" in meta:
 | 
				
			||||||
 | 
					                            enabled = meta["start_on_boot"]
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            enabled = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if "alias" in meta:
 | 
				
			||||||
 | 
					                            alias = meta["alias"]
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            alias = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        self.modules.append( ModuleV1( "modules/{}/".format(folder), code, enabled, alias ) )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        print("[INFO] reload_modules: successfully loaded {} as {}".format(folder, alias))
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        print("[WARN] reload_modules: module {} requires unsupported version ({} > 1), skipping...".format(folder, meta["version"]))
 | 
				
			||||||
 | 
					            except Exception as e:
 | 
				
			||||||
 | 
					                print("[ERROR] module_loader: error while loading module \"{}\" ({})".format(folder, e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reload_module(self, folder_name):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            meta_raw = readfile("modules/{}/meta.json".format(folder))
 | 
				
			||||||
 | 
					            if not meta_raw:
 | 
				
			||||||
 | 
					                print("[WARN] module_loader: no meta.json found in module folder \"{}\"".format(folder_name))
 | 
				
			||||||
 | 
					                return 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            meta = json.loads( readfile("modules/{}/meta.json".format(folder_name)) )
 | 
				
			||||||
 | 
					            if "version" in meta:
 | 
				
			||||||
 | 
					                if meta["version"] == 1:
 | 
				
			||||||
 | 
					                    if "index_file" in meta:
 | 
				
			||||||
 | 
					                        index_file = meta["index_file"]
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        index_file = "index.py"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    code = readfile( "modules/{}/{}".format(folder_name, index_file) )
 | 
				
			||||||
 | 
					                    if not code: # False both when readfile() returns False and when the code string is empty
 | 
				
			||||||
 | 
					                        print("[WARN] reload_modules: module {} does not have any code, skipping...".format(folder_name))
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                    if "start_on_boot" in meta:
 | 
				
			||||||
 | 
					                        enabled = meta["start_on_boot"]
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        enabled = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if "alias" in meta:
 | 
				
			||||||
 | 
					                        alias = meta["alias"]
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        alias = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    self.modules.append( ModuleV1( code, enabled, alias ) )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    print("[INFO] reload_modules: successfully loaded {} as {}".format(folder_name, alias))
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    print("[WARN] reload_modules: module {} requires unsupported version ({} > 1), skipping...".format(folder_name, meta["version"]))
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            print("[ERROR] module_loader: error while loading module \"{}\" ({})".format(folder_name, e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# message queue object to go back to synchronous message processing
 | 
				
			||||||
 | 
					#class MessageQueue:
 | 
				
			||||||
 | 
					#    def __init__(self):
 | 
				
			||||||
 | 
					#        print("[INFO] Initializing the message queue...")
 | 
				
			||||||
 | 
					#        self.queue = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# synchronous message processor
 | 
				
			||||||
 | 
					def queue_processor():
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        if len(message_queue) > 0:
 | 
				
			||||||
 | 
					            msg = message_queue[0]
 | 
				
			||||||
 | 
					            print("[DEBUG] queue_processor: {}".format(msg)) # debug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # modules will be used in here
 | 
				
			||||||
 | 
					            for mod in mcu.modules:
 | 
				
			||||||
 | 
					                if mod.enabled:
 | 
				
			||||||
 | 
					                    if mod.version == 1:
 | 
				
			||||||
 | 
					                        responce = mod.process(msg)
 | 
				
			||||||
 | 
					                        if len(responce) > 0:
 | 
				
			||||||
 | 
					                            updater.bot.send_message(chat_id = msg.chat.id, text = responce)
 | 
				
			||||||
 | 
					                            print("Responded using module {} ({}) with text: {}".format(mod.path, mod.alias, responce))
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            del message_queue[0]
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            if STOP_REQUESTED:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                time.sleep(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print("[INFO] queue_processor thread stops successfully")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# telegram bot processor
 | 
				
			||||||
 | 
					def message_handler(update, context):
 | 
				
			||||||
 | 
					    print("[DEBUG] Received new message") # just for testing
 | 
				
			||||||
 | 
					    message_queue.append(update.message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# --- Final stage ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# initializing services and queues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message_queue = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mcu = ModuleControlUnit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					processor_thread = threading.Thread( target = queue_processor, args = [] )
 | 
				
			||||||
 | 
					processor_thread.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# connecting to Telegram servers and listening for messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TOKEN = readfile("config/token")
 | 
				
			||||||
 | 
					if not TOKEN:
 | 
				
			||||||
 | 
					    print("[CRIT] Token has not been defined, quitting")
 | 
				
			||||||
 | 
					    sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# connect to Telegram servers
 | 
				
			||||||
 | 
					updater = Updater(TOKEN, use_context = True)
 | 
				
			||||||
 | 
					dispatcher = updater.dispatcher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# assign the handler for messages
 | 
				
			||||||
 | 
					dispatcher.add_handler(MessageHandler(Filters.text, message_handler))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# run the bot
 | 
				
			||||||
 | 
					updater.start_polling()
 | 
				
			||||||
 | 
					updater.idle()
 | 
				
			||||||
							
								
								
									
										1
									
								
								modules/echo-v1.0/index.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								modules/echo-v1.0/index.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					self.RESPONCE = self.MESSAGE["text"]
 | 
				
			||||||
							
								
								
									
										5
									
								
								modules/echo-v1.0/meta.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								modules/echo-v1.0/meta.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "version": 1,
 | 
				
			||||||
 | 
					    "index_file": "index.py",
 | 
				
			||||||
 | 
					    "start_on_boot": true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user