aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--commands/__init__.py2
-rw-r--r--commands/handling.py62
-rwxr-xr-xlaunch.sh5
-rw-r--r--lib/__init__.py58
-rw-r--r--main.py19
-rw-r--r--modules/builtins/eval.py60
-rw-r--r--modules/builtins/help.py10
-rw-r--r--requirements.txt1
9 files changed, 219 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ce2e17d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+config.ini
+*.session \ No newline at end of file
diff --git a/commands/__init__.py b/commands/__init__.py
new file mode 100644
index 0000000..dc26d49
--- /dev/null
+++ b/commands/__init__.py
@@ -0,0 +1,2 @@
+from commands.handling import handle_commands, load_commands
+
diff --git a/commands/handling.py b/commands/handling.py
new file mode 100644
index 0000000..46cdb66
--- /dev/null
+++ b/commands/handling.py
@@ -0,0 +1,62 @@
+import sys
+import traceback
+
+import importlib
+import os
+import pyrogram
+import re
+import types
+from pyrogram.api import types as tgtypes
+
+import lib
+
+PREFIX = "!"
+
+
+def load_module(module):
+ functions = [module.__dict__.get(a) for a in dir(module)
+ if isinstance(module.__dict__.get(a), types.FunctionType)]
+ for func in functions:
+ if lib.is_command(func):
+ lib.register_command(func)
+
+
+def load_commands(folder='modules'):
+ for dirname, dirnames, filenames in os.walk(folder):
+ for filename in filenames:
+ filename: str
+ if filename.endswith('.py'):
+ filename = filename[:-3]
+ pos = os.path.join(dirname, filename)
+ module = importlib.import_module(pos.replace('/', '.'))
+ load_module(module)
+
+
+def handle_commands(client: pyrogram.Client, update, users, chats):
+ if not (isinstance(update, tgtypes.UpdateNewMessage)
+ or isinstance(update, tgtypes.UpdateNewChannelMessage)
+ or isinstance(update, tgtypes.UpdateNewEncryptedMessage)):
+ return
+ update: tgtypes.UpdateNewMessage
+ message: tgtypes.Message = update.message
+ author_id = message.from_id
+ if author_id != client.user_id:
+ # do not react to other people
+ return
+ text: str = message.message
+ if text[:len(PREFIX)] != PREFIX:
+ return
+ parts = re.split(r'\s+', text)
+ if len(parts) < 1:
+ return
+ command = parts[0][1:]
+ args = parts[1:]
+ cmd_func = lib.commands[command.lower()]
+ ctx = lib.CommandContext(client=client, channel=message.to_id, args=args, message=message)
+ try:
+ cmd_func(ctx)
+ except KeyError:
+ ctx.respond('unknown command')
+ except Exception as e:
+ ctx.respond("unknown exception during execution. Error will be DM'd" + str(e))
+ print(traceback.format_exc(), file=sys.stderr)
diff --git a/launch.sh b/launch.sh
new file mode 100755
index 0000000..ab81ae7
--- /dev/null
+++ b/launch.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+DIRNAME=$(dirname $(readlink -f $0))
+cd ${DIRNAME}
+${DIRNAME}/venv/bin/python ${DIRNAME}/main.py
+cd /
diff --git a/lib/__init__.py b/lib/__init__.py
new file mode 100644
index 0000000..2c67b83
--- /dev/null
+++ b/lib/__init__.py
@@ -0,0 +1,58 @@
+from typing import List
+
+import pyrogram
+from pyrogram.api import types as tgtypes
+
+
+def property_decorator(key):
+ def decorator(value):
+ def wrapper(func):
+ setattr(func, key, value)
+ return func
+
+ return wrapper
+
+ return decorator
+
+
+name = property_decorator('name')
+description = property_decorator('description')
+
+
+def is_command(func):
+ return hasattr(func, 'name') and func.name is not None
+
+
+def get_command_name(func):
+ return func.name
+
+
+def get_command_description(func):
+ return func.description
+
+
+commands = {}
+
+
+def get_all_commands():
+ return commands.values()
+
+
+class CommandContext(object):
+ def __init__(self, client: pyrogram.Client, channel, args: List[str], message: tgtypes.Message):
+ import re
+ self.args = args
+ self.client = client
+ self.channel = channel
+ self.message = message
+ self.rest_content = re.sub('.*? ', '', message.message)
+ self.author = message.from_id
+
+ def respond(self, text):
+ self.client.send_message(self.channel, text=text)
+
+
+def register_command(func):
+ if not is_command(func):
+ return
+ commands[get_command_name(func)] = func
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..693f9d4
--- /dev/null
+++ b/main.py
@@ -0,0 +1,19 @@
+from pyrogram import Client
+
+from commands import handle_commands, load_commands
+
+
+def update_handler(client, update, users, chats):
+ handle_commands(client, update, users, chats)
+
+
+def main():
+ load_commands()
+ client = Client(session_name="userbot")
+ client.set_update_handler(update_handler)
+ client.start()
+ client.idle()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/modules/builtins/eval.py b/modules/builtins/eval.py
new file mode 100644
index 0000000..dfbdd57
--- /dev/null
+++ b/modules/builtins/eval.py
@@ -0,0 +1,60 @@
+import ast
+
+from lib import *
+
+
+@name('eval')
+@description('evals a given piece of python code')
+def eval_command(ctx: CommandContext):
+ try:
+ block = ast.parse(ctx.rest_content, mode='exec')
+ last = ast.Expression(block.body.pop().value)
+ except KeyboardInterrupt:
+ raise
+ except SystemExit:
+ raise
+ except BaseException as e:
+ ctx.respond("Compilation failed: %r" % e)
+ return
+
+ _globals, _locals = {}, {
+ 'ctx': ctx,
+ 'message': ctx.message,
+ 'client': ctx.client,
+ 'print':
+ lambda *content, stdout=False:
+ print(*content)
+ if stdout
+ else ctx.respond('\t'.join(map(str, content)))
+ }
+ try:
+ exec(compile(block, '<string>', mode='exec'), _globals, _locals)
+ except KeyboardInterrupt:
+ raise
+ except SystemExit:
+ raise
+ except BaseException as e:
+ ctx.respond("Evaluation failed: %r" % str(e))
+ return
+
+ try:
+ compiled = compile(last, '<string>', mode='eval')
+ except KeyboardInterrupt:
+ raise
+ except SystemExit:
+ raise
+ except BaseException as e:
+ ctx.respond("Last statement has to be an expression: %r" % str(e))
+ return
+
+ try:
+ result = eval(compiled, _globals, _locals)
+ except KeyboardInterrupt:
+ raise
+ except SystemExit:
+ raise
+ except BaseException as e:
+ ctx.respond("Evaluation failed: %r" % str(e))
+ return
+
+ ctx.respond("Evaluation succes: \n```\n%s\n```" % str(result))
diff --git a/modules/builtins/help.py b/modules/builtins/help.py
new file mode 100644
index 0000000..523d86d
--- /dev/null
+++ b/modules/builtins/help.py
@@ -0,0 +1,10 @@
+from lib import CommandContext, get_all_commands, name, description, get_command_name, get_command_description
+
+
+@name('help')
+@description('lists all commands with their descriptions')
+def help_command(ctx: CommandContext):
+ resp = ""
+ for command in get_all_commands():
+ resp += '%s - %s\n' % (get_command_name(command), get_command_description(command))
+ ctx.respond(resp)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..032294a
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+pyrogram[tgcrypto]