28 Commits
dev ... master

Author SHA1 Message Date
08672bdbbe Added very important spelling correction module 2023-11-09 12:49:10 +02:00
11acdb6fd4 generic-command-processor: add an easter egg for requesting the server link 2023-11-06 18:15:46 +02:00
e830bae282 add new module: help (allows to get manual pages for commands) 2023-11-04 13:48:40 +02:00
2a9bd28d65 add new module: tag-sub (allows to subscribe to events and get tagged once they happen) 2023-11-04 13:48:23 +02:00
2b8105d2a8 core: log responses to stdout first, then send as a Telegram message 2023-11-04 13:17:22 +02:00
a7bbb1c1cc core: harden message processing by catching wrong returns from v2 modules 2023-11-03 17:54:35 +02:00
e002dc46a2 update schedule 2023-10-30 10:07:23 +02:00
51307b5234 transliteration-decoder: implement proper HTML escaping and add new transliteration model 2023-10-29 21:13:48 +02:00
ec22c72afb core: harden the handling of received messages 2023-10-29 20:56:55 +02:00
79a307a7b5 core: add control option to check the message queue length 2023-10-10 00:15:21 +03:00
3e38c113d9 pingtools: fix leaderboard sorting 2023-10-09 23:48:08 +03:00
7d0d816ca8 generic-command-processor: add easter eggs and make the module case-insensitive 2023-10-09 23:10:03 +03:00
376ffd4957 core: add control commands for enabling and disabling modules on the fly 2023-10-03 16:57:49 +03:00
a530272408 add new module: generic-command-processor 2023-10-01 22:34:39 +03:00
807dee5af1 core: prioritize processing commands over processing all other messages 2023-09-27 08:27:38 +03:00
f591c8d54a core: implement adaptive delays during message processing and improve the control panel 2023-09-26 13:00:17 +03:00
48e672a7b0 pingtools: add the ping-pong minigame 2023-09-26 12:58:40 +03:00
b5a37acecb add new module: pingtools 2023-09-21 19:32:56 +03:00
187d990d2b translator: update translating server URL 2023-09-21 19:30:18 +03:00
151107aa81 core: fix response dropping when formatting is returned as an empty string 2023-09-21 19:29:53 +03:00
2446a20a5b update schedule 2023-09-19 18:52:54 +03:00
4502e7fbe1 Merge pull request 'transliteration-decoder: add automatic photo caption detection and let bot core deal with exceptions' (#11) from dev into master
Reviewed-on: dymik739/modular-bot-framework-for-telegram#11
2023-09-19 18:22:50 +03:00
34439e944b update schedule 2023-09-12 12:36:54 +03:00
dc8056abed Merge pull request 'Merge updates from dev branch to master' (#10) from dev into master
Reviewed-on: dymik739/modular-bot-framework-for-telegram#10
2023-09-12 12:32:45 +03:00
9bdf8d0775 update schedule 2023-09-09 15:15:30 +03:00
34f9b83a95 update schedule.json 2023-09-06 17:16:40 +03:00
87928df3f7 update schedule.json 2023-09-06 12:25:38 +03:00
baf815e74d update schedule 2023-09-05 20:14:18 +03:00
23 changed files with 631 additions and 74 deletions

148
main.py
View File

@@ -11,7 +11,12 @@ import traceback
# global variables # global variables
STOP_REQUESTED = False STOP_REQUESTED = False
DEBUG_MODE = False
DELAY_AFTER_RESPONSE = 3
DELAY_AFTER_MESSAGE = 0.1
DELAY_AFTER_IDLE = 1.0
lock = threading.Lock()
# some functions that increase readability of the code # some functions that increase readability of the code
def readfile(filename): def readfile(filename):
@@ -44,6 +49,8 @@ class ModuleV1:
def set_predefine(self): def set_predefine(self):
try: try:
if DEBUG_MODE:
print(f"Predefine on module v1 {self.alias} ({self.path})")
exec(self.predefine) exec(self.predefine)
except Exception as e: except Exception as e:
print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\" " print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\" "
@@ -55,11 +62,13 @@ class ModuleV1:
self.MESSAGE = msg self.MESSAGE = msg
try: try:
if DEBUG_MODE:
print(f"Calling module v1 {self.alias} ({self.path})")
exec(self.code) exec(self.code)
return self.RESPONSE, self.FORMAT return self.RESPONSE, self.FORMAT
except Exception as e: except Exception as e:
print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"")
print(f"[ERROR] module v1: traceback:\ntraceback.format_exc()") print(f"[ERROR] module v1: traceback:\n{traceback.format_exc()}")
return "", None return "", None
@@ -75,6 +84,8 @@ class ModuleV2:
# running the module # running the module
def process(self, msg): def process(self, msg):
try: try:
if DEBUG_MODE:
print(f"Calling module v2 {self.alias} ({self.path})")
return self.obj.process(msg, self.path) return self.obj.process(msg, self.path)
except Exception as e: except Exception as e:
print(f"[ERROR] module v2: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") print(f"[ERROR] module v2: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"")
@@ -166,16 +177,19 @@ def queue_processor():
try: try:
if len(message_queue) > 0: if len(message_queue) > 0:
msg = message_queue[0] msg = message_queue[0]
del message_queue[0]
print("[DEBUG] queue_processor: {}".format(msg)) # debug print("[DEBUG] queue_processor: {}".format(msg)) # debug
# check for control commands # check for control commands
if msg["chat"]["id"] == 575246355: if msg.chat.id == 575246355:
if msg["text"][0] == "$": if msg["text"][0] == "$":
command = msg["text"][1:].split(" ") command = msg["text"][1:].split()
if len(command) >= 2 and command[0] == "module": if len(command) >= 2 and command[0] == "module":
if command[1] == "reload": if command[1] == "reload":
print("[INFO] Module reloading triggered by a command") if len(command) == 2:
print("[INFO] Full module reloading triggered by a command")
updater.bot.send_message(msg.chat.id, f"Reloading all modules...")
# properly reload all v2 modules # properly reload all v2 modules
for mod in mcu.modules: for mod in mcu.modules:
@@ -184,55 +198,153 @@ def queue_processor():
del mcu.modules[:] del mcu.modules[:]
mcu.reload_modules() mcu.reload_modules()
else:
# TO DO: make it possible to reload individual modules by their
# alias or containing folder
pass
elif command[1] == "enable" and len(command) == 3:
if command[2] == "all":
for mod in mcu.modules:
mod.enabled = True
print(f"[INFO] module {mod.alias} was enabled")
else:
for mod in mcu.modules:
if mod.alias == command[2]:
mod.enabled = True
print(f"[INFO] module {mod.alias} was enabled")
elif command[1] == "disable" and len(command) == 3:
if command[2] == "all":
for mod in mcu.modules:
mod.enabled = False
print(f"[INFO] module {mod.alias} was disabled")
else:
for mod in mcu.modules:
if mod.alias == command[2]:
mod.enabled = False
print(f"[INFO] module {mod.alias} was disabled")
elif command[1] == "status" and len(command) == 3:
if command[2] == "all":
for mod in mcu.modules:
print(f"[INFO] module {mod.alias} is {mod.enabled}")
else:
for mod in mcu.modules:
if mod.alias == command[2]:
print(f"[INFO] module {mod.alias} is {mod.enabled}")
elif (2 <= len(command) <= 3) and command[0] == "delay":
l = len(command)
if command[1] == "response":
if l == 3:
try:
new_value = float(command[2])
global DELAY_AFTER_RESPONSE
DELAY_AFTER_RESPONSE = new_value
updater.bot.send_message(msg.chat.id, f"Set DELAY_AFTER_RESPONSE to {command[2]}")
except:
print(f"[WARN]: Cannot set DELAY_AFTER_RESPONSE to non-float value of {command[2]}")
updater.bot.send_message(msg.chat.id, f"[WARN]: Cannot set DELAY_AFTER_RESPONSE to non-float value of {command[2]}")
elif l == 2:
print(f"[INFO]: DELAY_AFTER_RESPONSE = {DELAY_AFTER_RESPONSE}")
updater.bot.send_message(msg.chat.id, f"[INFO]: DELAY_AFTER_RESPONSE = {DELAY_AFTER_RESPONSE}")
elif command[1] == "message":
if l == 3:
try:
new_value = float(command[2])
global DELAY_AFTER_MESSAGE
DELAY_AFTER_MESSAGE = new_value
updater.bot.send_message(msg.chat.id, f"Set DELAY_AFTER_MESSAGE to {command[2]}")
except:
print("[WARN]: Cannot set DELAY_AFTER_MESSAGE to non-float value of {command[2]}")
updater.bot.send_message(msg.chat.id, f"[WARN]: Cannot set DELAY_AFTER_MESSAGE to non-float value of {command[2]}")
elif l == 2:
print(f"[INFO]: DELAY_AFTER_MESSAGE = {DELAY_AFTER_MESSAGE}")
updater.bot.send_message(msg.chat.id, f"[INFO]: DELAY_AFTER_MESSAGE = {DELAY_AFTER_MESSAGE}")
elif command[1] == "idle":
if l == 3:
try:
new_value = float(command[2])
global DELAY_AFTER_IDLE
DELAY_AFTER_IDLE = new_value
except:
print("[WARN]: Cannot set DELAY_AFTER_IDLE to non-float value of {command[2]}")
updater.bot.send_message(msg.chat.id, f"[WARN]: Cannot set DELAY_AFTER_MESSAGE to non-float value of {command[2]}")
elif l == 2:
print(f"[INFO]: DELAY_AFTER_IDLE = {DELAY_AFTER_IDLE}")
updater.bot.send_message(msg.chat.id, f"[INFO]: DELAY_AFTER_IDLE = {DELAY_AFTER_IDLE}")
elif len(command) == 2 and command[0] == "queue":
if command[1] == "size":
print(f"[INFO]: Queue length is {len(message_queue)}")
updater.bot.send_message(msg.chat.id, f"[INFO]: Queue length is {len(message_queue)}")
elif len(command) == 2 and command[0] == "debug":
global DEBUG_MODE
if command[1] == "on":
DEBUG_MODE = True
else:
DEBUG_MODE = False
del message_queue[0]
continue continue
# modules are used in here # modules are used in here
for mod in mcu.modules: for mod in mcu.modules:
if mod.enabled: if mod.enabled:
if mod.version in [1, 2]: if mod.version in [1, 2]:
try:
response, formatting = mod.process(msg) response, formatting = mod.process(msg)
except Exception as e:
print(f"Module {mod.alias} ({mod.path}) failed to do a proper return, skipping...")
continue
if response: if response:
if formatting == None: if not formatting:
print(f"Responding using module {mod.path} ({mod.alias}) with text: {response}")
updater.bot.send_message(chat_id=msg.chat.id, text=response, updater.bot.send_message(chat_id=msg.chat.id, text=response,
disable_web_page_preview=True) disable_web_page_preview=True)
print(f"Responded using module {mod.path} ({mod.alias}) with text: {response}") time.sleep(DELAY_AFTER_RESPONSE)
break break
elif formatting in ["Markdown", "MarkdownV2", "HTML"]: elif formatting in ["Markdown", "MarkdownV2", "HTML"]:
print(f"Responding using module {mod.path} ({mod.alias}) with text (using {formatting}): {response}")
updater.bot.send_message(chat_id=msg.chat.id, text=response, updater.bot.send_message(chat_id=msg.chat.id, text=response,
disable_web_page_preview=True, disable_web_page_preview=True,
parse_mode=formatting) parse_mode=formatting)
print(f"Responded using module {mod.path} ({mod.alias}) with text (using {formatting}): {response}") time.sleep(DELAY_AFTER_RESPONSE)
break break
del message_queue[0] time.sleep(DELAY_AFTER_MESSAGE)
time.sleep(0.1)
else: else:
if STOP_REQUESTED: if STOP_REQUESTED:
break break
else: else:
time.sleep(1) time.sleep(DELAY_AFTER_IDLE)
except Exception as e: except Exception as e:
print(f"[ERROR] queue_processor: current message queue: {message_queue}") print(f"[ERROR] queue_processor: current message queue: {message_queue}")
print(f"[ERROR] Traceback:\n{traceback.format_exc()}") print(f"[ERROR] Traceback:\n{traceback.format_exc()}")
print(f"[ERROR] queue_processor: error while processing message ({e}), trying to delete it...") print(f"[ERROR] queue_processor: error while processing message ({e})...")
try: print("[INFO] queue_processor: skipped broken message")
del message_queue[0]
print("[INFO] queue_processor: deleted broken message from the queue")
except:
print("[WARN] queue_processor: message seems absent, whatever")
print("[INFO] queue_processor thread stops successfully") print("[INFO] queue_processor thread stops successfully")
# telegram bot processor # telegram bot processor
def message_handler(update, context): def message_handler(update, context):
global lock
with lock:
if update.message and update.message.text.__class__ == str and update.message.text.startswith("$"):
print("[DEBUG] Received new message with high priority") # just for testing
message_queue.insert(0, update.message)
elif update.message:
print("[DEBUG] Received new message") # just for testing print("[DEBUG] Received new message") # just for testing
message_queue.append(update.message) message_queue.append(update.message)
else:
print(f"[DEBUG] Received {update.message} instead of a message")
# --- Final stage --- # --- Final stage ---

View File

@@ -195,7 +195,7 @@ def queue_processor():
response, formatting = mod.process(msg) response, formatting = mod.process(msg)
if response: if response:
if formatting == None: if not formatting:
print(f"Responded using module {mod.path} ({mod.alias}) with text: {response}") print(f"Responded using module {mod.path} ({mod.alias}) with text: {response}")
break break
elif formatting in ["Markdown", "MarkdownV2", "HTML"]: elif formatting in ["Markdown", "MarkdownV2", "HTML"]:

View File

@@ -19,7 +19,7 @@
{ {
"name": "Психологія", "name": "Психологія",
"teacher": "Волянюк Н. Ю.", "teacher": "Волянюк Н. Ю.",
"link": "https://us04web.zoom.us/j/6762396563?pwd=L1EvTmpFZHBSdkRHUjZyRG95SFl4QT0", "link": "https://us04web.zoom.us/j/6762396563?pwd=L1EvTmpFZHBSdkRHUjZyRG95SFl4QT09",
"type": "lec", "type": "lec",
"selectable": true "selectable": true
}, },
@@ -44,10 +44,9 @@
"12:20": { "12:20": {
"name": "Інженерія програмного забезпечення", "name": "Інженерія програмного забезпечення",
"teacher": "Васильєва Марія Давидівна", "teacher": "Васильєва Марія Давидівна",
"link": "https://zoom.us/wc/88696149166/join?from=join&_x_zm_rtaid=qhdJKhYLQNakh-zwxMG4lg.1693903841334.ad606145c892a54a4b450526e2394cbe&_x_zm_rhtaid=531", "link": "https://do.ipo.kpi.ua/mod/bigbluebuttonbn/view.php?id=171039",
"type": "lab", "type": "lab",
"selectable": false, "selectable": false
"notice": "Код: 4VHkdw"
}, },
"14:15": { "14:15": {
@@ -77,7 +76,7 @@
"12:20": { "12:20": {
"name": "Інженерія програмного забезпечення", "name": "Інженерія програмного забезпечення",
"teacher": "Васильєва Марія Давидівна", "teacher": "Васильєва Марія Давидівна",
"link": "(посилання відсутнє!)", "link": "https://do.ipo.kpi.ua/mod/bigbluebuttonbn/view.php?id=171039",
"type": "lec", "type": "lec",
"selectable": false "selectable": false
} }
@@ -86,14 +85,14 @@
"10:25": { "10:25": {
"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення", "name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення",
"teacher": "Стаматієва Вікторія В'ячеславівна", "teacher": "Стаматієва Вікторія В'ячеславівна",
"link": "(посилання відсутнє!)", "link": "https://us04web.zoom.us/j/2313886209?pwd=dnZHanV3cU9LUXJBVWYyYVArUFg5dz09",
"type": "prac", "type": "prac",
"selectable": false "selectable": false
}, },
"12:20": { "12:20": {
"name": "Практичний курс іноземної мови. Частина 2", "name": "Практичний курс іноземної мови. Частина 2",
"teacher": "Шевченко Ольга Миколаївна", "teacher": "Шевченко Ольга Миколаївна",
"link": "(старе посилання!) https://meet.google.com/bwg-pdnr-evh", "link": "https://meet.google.com/tno-cxef-zyi",
"type": "prac", "type": "prac",
"selectable": false "selectable": false
}, },
@@ -116,7 +115,7 @@
"8:30": { "8:30": {
"name": "Вступ до філософії", "name": "Вступ до філософії",
"teacher": "Руденко Тамара Петрівна", "teacher": "Руденко Тамара Петрівна",
"link": "(посилання відсутнє!)", "link": "https://zoom.us/j/9358038101?pwd=d0pwUHRDY0dxbngrU09PYll6UXpNZz09",
"type": "lec", "type": "lec",
"selectable": false "selectable": false
}, },
@@ -130,7 +129,7 @@
"12:20": { "12:20": {
"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення", "name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення",
"teacher": "Овчар Раїса Федорівна", "teacher": "Овчар Раїса Федорівна",
"link": "(посилання відсутнє!)", "link": "https://us02web.zoom.us/j/84532519615?pwd=eDFRMWtJTkxKcklpa1JUSjFmZHNyUT09",
"type": "lec", "type": "lec",
"selectable": false "selectable": false
} }
@@ -142,14 +141,14 @@
{ {
"name": "Психологія", "name": "Психологія",
"teacher": "Сербова О. В.", "teacher": "Сербова О. В.",
"link": "https://us04web.zoom.us/j/6762396563?pwd=L1EvTmpFZHBSdkRHUjZyRG95SFl4QT09", "link": "https://us05web.zoom.us/j/9299459744?pwd=Z3VQdWEvQ0tyc3pMbzl2bHN6Y1VlUT09",
"type": "prac", "type": "prac",
"selectable": true "selectable": true
}, },
{ {
"name": "Психологія конфлікту", "name": "Психологія конфлікту",
"teacher": "Кононець М. О.", "teacher": "Кононець М. О.",
"link": "https://zoom.us/j/5175581158?pwd=UlhFY3lBOUUrNG9pclRVNndTNTZzQT09", "link": "https://zoom.us/j/9953120638?pwd=WGZsYUhPK2hxbUc4YVJmT0lhdysyZz09",
"type": "lec", "type": "lec",
"selectable": true "selectable": true
} }
@@ -159,14 +158,14 @@
{ {
"name": "Політична наука: конфліктологічний підхід", "name": "Політична наука: конфліктологічний підхід",
"teacher": "Северинчик О. П.", "teacher": "Северинчик О. П.",
"link": "https://zoom.us/j/5175581158?pwd=UlhFY3lBOUUrNG9pclRVNndTNTZzQT09", "link": "https://us04web.zoom.us/j/2279372490?pwd=bHR5QmpCT0tvQXJMLzRzaldHbFZ3dz09",
"type": "prac", "type": "prac",
"selectable": true "selectable": true
}, },
{ {
"name": "Захист персональних даних: стандарти ЄС та Ради Європи", "name": "Захист персональних даних: стандарти ЄС та Ради Європи",
"teacher": "Самчинська О. А.", "teacher": "Самчинська О. А.",
"link": "(посилання відсутнє!)", "link": "https://us04web.zoom.us/j/72149205587?pwd=Ld2Xj7RORYEwnUYauB5yEbATwwsNan.1",
"type": "prac", "type": "prac",
"selectable": true "selectable": true
} }
@@ -175,7 +174,7 @@
"14:15": { "14:15": {
"name": "Розумні міста", "name": "Розумні міста",
"teacher": "Чукут Світлана Анатоліївна", "teacher": "Чукут Світлана Анатоліївна",
"link": "(посилання відсутнє!)", "link": "https://zoom.us/j/5439919039?pwd=Um8wWHV4ZjZpallCWkpVQ08wZGNzdz09",
"type": "lec", "type": "lec",
"selectable": true "selectable": true
} }
@@ -184,7 +183,7 @@
"10:25": { "10:25": {
"name": "Вступ до філософії", "name": "Вступ до філософії",
"teacher": "Руденко Тамара Петрівна", "teacher": "Руденко Тамара Петрівна",
"link": "(посилання відсутнє!)", "link": "https://zoom.us/j/9358038101?pwd=d0pwUHRDY0dxbngrU09PYll6UXpNZz09",
"type": "prac", "type": "prac",
"selectable": false "selectable": false
}, },
@@ -214,14 +213,14 @@
"12:20": { "12:20": {
"name": "Інженерія програмного забезпечення", "name": "Інженерія програмного забезпечення",
"teacher": "Васильєва Марія Давидівна", "teacher": "Васильєва Марія Давидівна",
"link": "(посилання відсутнє!)", "link": "https://do.ipo.kpi.ua/mod/bigbluebuttonbn/view.php?id=171039",
"type": "lec", "type": "lec",
"selectable": false "selectable": false
}, },
"14:15": { "14:15": {
"name": "Інженерія програмного забезпечення", "name": "Інженерія програмного забезпечення",
"teacher": "Васильєва Марія Давидівна", "teacher": "Васильєва Марія Давидівна",
"link": "(посилання відсутнє!)", "link": "https://do.ipo.kpi.ua/mod/bigbluebuttonbn/view.php?id=171039",
"type": "lec", "type": "lec",
"selectable": false "selectable": false
} }
@@ -230,21 +229,21 @@
"8:30": { "8:30": {
"name": "Вступ до операційної системи Linux", "name": "Вступ до операційної системи Linux",
"teacher": "Алєнін Олег Ігорович", "teacher": "Алєнін Олег Ігорович",
"link": "(посилання відсутнє!)", "link": "https://us04web.zoom.us/j/4122071690?pwd=bANFi3fk9pWvRu9TSBRGzfxFHuEkZC.1",
"type": "lab", "type": "lab",
"selectable": false "selectable": false
}, },
"10:25": { "10:25": {
"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення", "name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення",
"teacher": "Стаматієва Вікторія В'ячеславівна", "teacher": "Стаматієва Вікторія В'ячеславівна",
"link": "(старе посилання!) https://us04web.zoom.us/j/2313886209?pwd=dnZHanV3cU9LUXJBVWYyYVArUFg5dz09", "link": "https://us04web.zoom.us/j/2313886209?pwd=dnZHanV3cU9LUXJBVWYyYVArUFg5dz09",
"type": "prac", "type": "prac",
"selectable": false "selectable": false
}, },
"12:20": { "12:20": {
"name": "Практичний курс іноземної мови. Частина 2", "name": "Практичний курс іноземної мови. Частина 2",
"teacher": "Шевченко Ольга Миколаївна", "teacher": "Шевченко Ольга Миколаївна",
"link": "(старе посилання!) https://meet.google.com/bwg-pdnr-evh", "link": "https://meet.google.com/tno-cxef-zyi",
"type": "prac", "type": "prac",
"selectable": false "selectable": false
}, },
@@ -258,8 +257,8 @@
}, },
{ {
"name": "Розумні міста", "name": "Розумні міста",
"teacher": "Чукут С. А.", "teacher": "Чукут Світлана Анатоліївна",
"link": "(посилання відсутні!)", "link": "https://zoom.us/j/5439919039?pwd=Um8wWHV4ZjZpallCWkpVQ08wZGNzdz09",
"type": "prac", "type": "prac",
"selectable": true "selectable": true
} }
@@ -276,7 +275,7 @@
"12:20": { "12:20": {
"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення", "name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення",
"teacher": "Овчар Раїса Федорівна", "teacher": "Овчар Раїса Федорівна",
"link": "(посилання відсутнє!)", "link": "https://us02web.zoom.us/j/84532519615?pwd=eDFRMWtJTkxKcklpa1JUSjFmZHNyUT09",
"type": "lec", "type": "lec",
"selectable": false "selectable": false
} }

View File

@@ -5,51 +5,51 @@
"14:15": {"name": "Основи електронного урядування (лекція)", "teacher": "Чукут Світлана Анатоліївна", "link": "(посилання відсутнє!)"} "14:15": {"name": "Основи електронного урядування (лекція)", "teacher": "Чукут Світлана Анатоліївна", "link": "(посилання відсутнє!)"}
}, },
{ {
"12:20": {"name": "Інженерія програмного забезпечення (лабораторна)", "teacher": "Васильєва Марія Давидівна", "link": "https://zoom.us/wc/88696149166/join?from=join&_x_zm_rtaid=qhdJKhYLQNakh-zwxMG4lg.1693903841334.ad606145c892a54a4b450526e2394cbe&_x_zm_rhtaid=531"}, "12:20": {"name": "Інженерія програмного забезпечення (лабораторна)", "teacher": "Васильєва Марія Давидівна", "link": "https://do.ipo.kpi.ua/mod/bigbluebuttonbn/view.php?id=171039"},
"14:15": {"name": "Теорія електричних кіл та сигналів (лабораторна)", "teacher": "Лободзинський В. Ю. & Ілліна О. О.", "link": "https://meet.google.com/gwx-sshq-sqb"} "14:15": {"name": "Теорія електричних кіл та сигналів (лабораторна)", "teacher": "Лободзинський В. Ю. & Ілліна О. О.", "link": "https://meet.google.com/gwx-sshq-sqb"}
}, },
{ {
"8:30": {"name": "Теорія ймовірності та математична статистика (лекція)", "teacher": "Марковський Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-9ru-7vc"}, "8:30": {"name": "Теорія ймовірності та математична статистика (лекція)", "teacher": "Марковський Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-9ru-7vc"},
"10:25": {"name": "Вступ до операційної системи Linux (лекція)", "teacher": "Роковий Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-knq-z9h-pyl"}, "10:25": {"name": "Вступ до операційної системи Linux (лекція)", "teacher": "Роковий Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-knq-z9h-pyl"},
"12:20": {"name": "Інженерія програмного забезпечення (лекція)", "teacher": "Васильєва Марія Давидівна", "link": "(посилання відсутнє!)"} "12:20": {"name": "Інженерія програмного забезпечення (лекція)", "teacher": "Васильєва Марія Давидівна", "link": "https://do.ipo.kpi.ua/mod/bigbluebuttonbn/view.php?id=171039"}
}, },
{ {
"10:25": {"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення (практика)", "teacher": "Стаматієва Вікторія В'ячеславівна", "link": "(посилання відсутнє!)"}, "10:25": {"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення (практика)", "teacher": "Стаматієва Вікторія В'ячеславівна", "link": "https://us04web.zoom.us/j/2313886209?pwd=dnZHanV3cU9LUXJBVWYyYVArUFg5dz09"},
"12:20": {"name": "Практичний курс іноземної мови. Частина 2 (практика)", "teacher": "Шевченко Ольга Миколаївна", "link": "(старе посилання!) https://meet.google.com/bwg-pdnr-evh"}, "12:20": {"name": "Практичний курс іноземної мови. Частина 2 (практика)", "teacher": "Шевченко Ольга Миколаївна", "link": "https://meet.google.com/tno-cxef-zyi"},
"14:15": {"name": "Соціальна психологія (практика)", "teacher": "Блохіна Ірина Олександрівна", "link": "(посилання відсутнє!)"}, "14:15": {"name": "Соціальна психологія (практика)", "teacher": "Блохіна Ірина Олександрівна", "link": "(посилання відсутнє!)"},
"16:10": {"name": "Основи електронного урядування (практика)", "teacher": "Чукут Світлана Анатоліївна", "link": "(посилання відсутнє!)"} "16:10": {"name": "Основи електронного урядування (практика)", "teacher": "Чукут Світлана Анатоліївна", "link": "(посилання відсутнє!)"}
}, },
{ {
"8:30": {"name": "Вступ до філософії (лекція)", "teacher": "Руденко Тамара Петрівна", "link": "(посилання відсутнє!)"}, "8:30": {"name": "Вступ до філософії (лекція)", "teacher": "Руденко Тамара Петрівна", "link": "https://zoom.us/j/9358038101?pwd=d0pwUHRDY0dxbngrU09PYll6UXpNZz09"},
"10:25": {"name": "Теорія електричних кіл та сигналів (лекція)", "teacher": "Лободзинський Вадим Юрійович", "link": "https://meet.google.com/gwx-sshq-sqb"}, "10:25": {"name": "Теорія електричних кіл та сигналів (лекція)", "teacher": "Лободзинський Вадим Юрійович", "link": "https://meet.google.com/gwx-sshq-sqb"},
"12:20": {"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення (лекція)", "teacher": "Овчар Раїса Федорівна", "link": "(посилання відсутнє!)"} "12:20": {"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення (лекція)", "teacher": "Овчар Раїса Федорівна", "link": "https://us02web.zoom.us/j/84532519615?pwd=eDFRMWtJTkxKcklpa1JUSjFmZHNyUT09"}
}, },
{}, {},
{}, {},
{ {
"10:25": {"name": "Психологія & Психологія конфлікту (практики)", "teacher": "Сербова О. В. & Кононець М. О.", "link": "https://us04web.zoom.us/j/6762396563?pwd=L1EvTmpFZHBSdkRHUjZyRG95SFl4QT09 & https://zoom.us/j/5175581158?pwd=UlhFY3lBOUUrNG9pclRVNndTNTZzQT09"}, "10:25": {"name": "Психологія (практика) & Психологія конфлікту (лекція)", "teacher": "Сербова О. В. & Кононець М. О.", "link": "https://us05web.zoom.us/j/9299459744?pwd=Z3VQdWEvQ0tyc3pMbzl2bHN6Y1VlUT09 & https://zoom.us/j/9953120638?pwd=WGZsYUhPK2hxbUc4YVJmT0lhdysyZz09"},
"12:20": {"name": "Політична наука: конфліктологічний підхід & Захист персональних даних: стандарти ЄС та Ради Європи (практики)", "teacher": "Северинчик О. П. & Самчинська О. А.", "link": "https://zoom.us/j/5175581158?pwd=UlhFY3lBOUUrNG9pclRVNndTNTZzQT09 & (посилання відсутнє!)"}, "12:20": {"name": "Політична наука: конфліктологічний підхід & Захист персональних даних: стандарти ЄС та Ради Європи (практики)", "teacher": "Северинчик О. П. & Самчинська О. А.", "link": "(посилання відсутнє!) & https://us04web.zoom.us/j/72149205587?pwd=Ld2Xj7RORYEwnUYauB5yEbATwwsNan.1"},
"14:15": {"name": "Розумні міста (лекція)", "teacher": "Чукут Світлана Анатоліївна", "link": "(посилання відсутнє!)"} "14:15": {"name": "Розумні міста (лекція)", "teacher": "Чукут Світлана Анатоліївна", "link": "https://zoom.us/j/5439919039?pwd=Um8wWHV4ZjZpallCWkpVQ08wZGNzdz09"}
}, },
{ {
"10:25": {"name": "Вступ до філософії (практика)", "teacher": "Руденко Тамара Петрівна", "link": "(посилання відсутнє!)"}, "10:25": {"name": "Вступ до філософії (практика)", "teacher": "Руденко Тамара Петрівна", "link": "https://zoom.us/j/9358038101?pwd=d0pwUHRDY0dxbngrU09PYll6UXpNZz09"},
"14:15": {"name": "Теорія ймовірності та математична статистика (практика)", "teacher": "Марковський Олександр Петрович", "link": "(посилання відсутнє!)"} "14:15": {"name": "Теорія ймовірності та математична статистика (практика)", "teacher": "Марковський Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-9ru-7vc"}
}, },
{ {
"8:30": {"name": "Теорія ймовірності та математична статистика (лекція)", "teacher": "Марковський Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-9ru-7vc"}, "8:30": {"name": "Теорія ймовірності та математична статистика (лекція)", "teacher": "Марковський Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-9ru-7vc"},
"10:25": {"name": "Вступ до операційної системи Linux (лекція)", "teacher": "Роковий Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-knq-z9h-pyl"}, "10:25": {"name": "Вступ до операційної системи Linux (лекція)", "teacher": "Роковий Олександр Петрович", "link": "https://bbb.comsys.kpi.ua/b/ole-knq-z9h-pyl"},
"12:20": {"name": "Інженерія програмного забезпечення (лекція)", "teacher": "Васильєва Марія Давидівна", "link": "(посилання відсутнє!)"}, "12:20": {"name": "Інженерія програмного забезпечення (лекція)", "teacher": "Васильєва Марія Давидівна", "link": "https://do.ipo.kpi.ua/mod/bigbluebuttonbn/view.php?id=171039"},
"14:15": {"name": "Інженерія програмного забезпечення (лекція)", "teacher": "Васильєва Марія Давидівна", "link": "(посилання відсутнє!)"} "14:15": {"name": "Інженерія програмного забезпечення (лекція)", "teacher": "Васильєва Марія Давидівна", "link": "https://do.ipo.kpi.ua/mod/bigbluebuttonbn/view.php?id=171039"}
}, },
{ {
"8:30": {"name": "Вступ до операційної системи Linux (лабораторна)", "teacher": "Алєнін Олег Ігорович", "link": "(посилання відсутнє!)"}, "8:30": {"name": "Вступ до операційної системи Linux (лабораторна)", "teacher": "Алєнін Олег Ігорович", "link": "https://us04web.zoom.us/j/4122071690?pwd=bANFi3fk9pWvRu9TSBRGzfxFHuEkZC.1"},
"10:25": {"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення (практика)", "teacher": "Стаматієва Вікторія В'ячеславівна", "link": "(старе посилання!) https://us04web.zoom.us/j/2313886209?pwd=dnZHanV3cU9LUXJBVWYyYVArUFg5dz09"}, "10:25": {"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення (практика)", "teacher": "Стаматієва Вікторія В'ячеславівна", "link": "https://us04web.zoom.us/j/2313886209?pwd=dnZHanV3cU9LUXJBVWYyYVArUFg5dz09"},
"12:20": {"name": "Практичний курс іноземної мови. Частина 2 (практика)", "teacher": "Шевченко Ольга Миколаївна", "link": "(старе посилання!) https://meet.google.com/bwg-pdnr-evh"}, "12:20": {"name": "Практичний курс іноземної мови. Частина 2 (практика)", "teacher": "Шевченко Ольга Миколаївна", "link": "https://meet.google.com/tno-cxef-zyi"},
"14:15": {"name": "Соціальна психологія (лекція) & Розумні міста (практика)", "teacher": "Винославська О. В. & Чукут С. А.", "link": "(посилання відсутні!)"} "14:15": {"name": "Соціальна психологія (лекція) & Розумні міста (практика)", "teacher": "Винославська О. В. & Чукут С. А.", "link": "(посилання відсутнє!) & https://zoom.us/j/5439919039?pwd=Um8wWHV4ZjZpallCWkpVQ08wZGNzdz09"}
}, },
{ {
"10:25": {"name": "Теорія електричних кіл та сигналів (лекція)", "teacher": "Лободзинський Вадим Юрійович", "link": "https://meet.google.com/gwx-sshq-sqb"}, "10:25": {"name": "Теорія електричних кіл та сигналів (лекція)", "teacher": "Лободзинський Вадим Юрійович", "link": "https://meet.google.com/gwx-sshq-sqb"},
"12:20": {"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення (лекція)", "teacher": "Овчар Раїса Федорівна", "link": "(посилання відсутнє!)"} "12:20": {"name": "Вища математика. Частина 3. Ряди. Теорія функцій комплексної змінної. Операційне числення (лекція)", "teacher": "Овчар Раїса Федорівна", "link": "https://us02web.zoom.us/j/84532519615?pwd=eDFRMWtJTkxKcklpa1JUSjFmZHNyUT09"}
}, },
{}, {},
{} {}

View File

@@ -0,0 +1,6 @@
{
"s": "Головна сторінка сервера: http://10.1.1.1:12010/",
"ss": "Головна сторінка сервера: http://10.1.1.1:12010/",
"пари_для_артема": "🧐",
"пари_для_артема_хоч_іеаавтра": "🤨"
}

View File

@@ -0,0 +1,6 @@
command_storage = json.loads(open(self.path + "data.json").read())
received_command = self.MESSAGE.text[1:]
if received_command.lower() in command_storage:
self.RESPONSE = command_storage[received_command.lower()]

View File

@@ -0,0 +1,6 @@
{
"version": 1,
"index_file": "index.py",
"start_on_boot": true,
"alias": "generic-command-processor"
}

View File

@@ -0,0 +1,5 @@
Usage: !hardsub [event_name]
Permanently subscribes to [event_name]. If blank, uses "default".
To unsubscribe, use !hardunsub
For one-time subscriptions, use !sub

View File

@@ -0,0 +1,5 @@
Usage: !hardunsub [event_name]
Remove permanent subscription to [event_name]. If blank, uses "default".
To remove one-time subscription, use !unsub
To add permanent subscription, use !hardsub

View File

@@ -0,0 +1,5 @@
Usage:
!schedule-ctl list - see all settings
!schedule-ctl get <setting> - see specific personal setting
!schedule-ctl set <setting> <value> - change <setting> to <value>
!schedule-ctl clear <setting> - restore <setting> to default value

7
modules/help/db/sub.txt Normal file
View File

@@ -0,0 +1,7 @@
Usage: !sub [event_name]
Soft-subscribes to [event_name]. If black, uses "default".
If you want to remove soft substription, use !unsub
Soft subscription is automatically removed after the event is triggered once.
If you want to subscribe permanently, use !hardsub

View File

@@ -0,0 +1,5 @@
Usage: !unsub [event_name]
Removes one-time subscription to [event_name]. If blank, uses "default".
Does not affect hard subscriptions. To remove hard subscription, use !hardunsub
To add one-time supscription, use !sub

54
modules/help/index.py Normal file
View File

@@ -0,0 +1,54 @@
import json
import os
module_path = ""
def readfile(filename):
if os.path.exists(module_path + filename):
return open(module_path + filename).read()
else:
return False
def list_entries(search_query):
files = []
for p, d, f in os.walk(module_path + "db/"):
for i in f:
entry_name = f"{p.split('/', 3)[3]}/{i}"
if search_query in entry_name:
files.append(f"{p.split('/', 3)[3]}/{i}".rsplit(".", 1)[0].replace("/", ".").lstrip("."))
files.sort()
return files
def process(message, path):
if message.text[:5] != "!help":
return None, None
global module_path
module_path = path
cmd = message.text[1:].split()
if len(cmd) == 1:
return "Usage:\n```\n!help show <entry> - displays specified manual entry\n!help list [query] - lists out available entries (optionally, filtered by [query])```", "Markdown"
elif cmd[1] == "list":
if len(cmd) == 2:
return "Available entries:\n" + "\n".join(list_entries("")), None
else:
return f"Found entries for {cmd[2]}:\n" + "\n".join(list_entries(cmd[2])), None
elif cmd[1] == "show":
if len(cmd) >= 3:
result = ""
for entry_name in cmd[2:]:
data = readfile("db/" + entry_name.replace(".", "/") + ".txt")
if data:
result += f"Manual page for {entry_name}:\n```\n{data}```\n"
else:
result += f"No manual found for {entry_name}\n\n"
return result, "Markdown"
else:
return "Usage: !help show <entry> - displays specified manual entry", None

6
modules/help/meta.json Normal file
View File

@@ -0,0 +1,6 @@
{
"start_on_boot": true,
"alias": "help",
"version": 2,
"index_file": "index.py"
}

129
modules/pingtools/index.py Normal file
View File

@@ -0,0 +1,129 @@
msg = self.MESSAGE.text.lower()
if msg == "!ping":
current_time = time.time()
delay = current_time - 7200 - float(self.MESSAGE.date.strftime('+%s'))
self.RESPONSE = f"Pong in {delay} seconds"
if not os.path.exists(self.path + "scoreboard/"):
os.mkdir(self.path + "scoreboard/")
def append_score(path, name, data):
open(path + f"scoreboard/{name}", "a").write(data)
user_id = self.MESSAGE.from_user.id
chat_id = self.MESSAGE.chat.id
first_name = self.MESSAGE.from_user.first_name
last_name = self.MESSAGE.from_user.last_name
username = self.MESSAGE.from_user.username
append_score(self.path, self.MESSAGE.from_user.id, f"{current_time} {user_id} {chat_id} {first_name} {last_name} {username} {delay}\n")
elif msg == "!pong":
current_time = time.time()
delay = current_time - 7200 - float(self.MESSAGE.date.strftime('+%s'))
self.RESPONSE = f"Ping in {delay} seconds"
if not os.path.exists(self.path + "pong-scoreboard/"):
os.mkdir(self.path + "pong-scoreboard/")
def append_score(path, name, data):
open(path + f"pong-scoreboard/{name}", "a").write(data)
user_id = self.MESSAGE.from_user.id
chat_id = self.MESSAGE.chat.id
first_name = self.MESSAGE.from_user.first_name
last_name = self.MESSAGE.from_user.last_name
username = self.MESSAGE.from_user.username
append_score(self.path, self.MESSAGE.from_user.id, f"{current_time} {user_id} {chat_id} {first_name} {last_name} {username} {delay}\n")
elif msg == "!pingtop":
def read_score(path, name):
if os.path.exists(path + f"scoreboard/{name}"):
return open(path + f"scoreboard/{name}").read()
else:
return None
if not os.path.exists(self.path + "scoreboard/"):
os.mkdir(self.path + "scoreboard/")
results = []
for name in os.listdir(self.path + "scoreboard/"):
data = [i for i in read_score(self.path, name).split("\n") if i != ""]
data.sort(key = lambda x: float(x.split()[6]))
results.append(data[0].split())
print(results)
results.sort(key = lambda x: float(x[6]))
self.RESPONSE = "<u>Ping top</u>:\n" + "\n".join( [f"{i+1}. {v[3]} {v[4]} ({v[5]}) - {v[6]}" for i, v in enumerate(results[:10])] )
self.FORMAT = "HTML"
elif msg == "!pongtop":
def read_score(path, name):
if os.path.exists(path + f"pong-scoreboard/{name}"):
return open(path + f"pong-scoreboard/{name}").read()
else:
return None
if not os.path.exists(self.path + "pong-scoreboard/"):
os.mkdir(self.path + "pong-scoreboard/")
results = []
for name in os.listdir(self.path + "pong-scoreboard/"):
data = [i for i in read_score(self.path, name).split("\n") if i != ""]
data.sort(key = lambda x: float(x.split()[6]))
results.append(data[0].split())
print(results)
results.sort(key = lambda x: float(x[6]))
self.RESPONSE = "<u>Pong top</u>:\n" + "\n".join( [f"{i+1}. {v[3]} {v[4]} ({v[5]}) - {v[6]}" for i, v in enumerate(results[:10])] )
self.FORMAT = "HTML"
elif msg == "!pingantitop":
def read_score(path, name):
if os.path.exists(path + f"scoreboard/{name}"):
return open(path + f"scoreboard/{name}").read()
else:
return None
if not os.path.exists(self.path + "scoreboard/"):
os.mkdir(self.path + "scoreboard/")
results = []
for name in os.listdir(self.path + "scoreboard/"):
data = [i for i in read_score(self.path, name).split("\n") if i != ""]
data.sort(key = lambda x: float(x.split()[6]))
results.append(data[-1].split())
print(results)
results.sort(key = lambda x: float(x[6]))
results = results[::-1]
self.RESPONSE = "<u>Ping antitop</u>:\n" + "\n".join( [f"{i+1}. {v[3]} {v[4]} ({v[5]}) - {v[6]}" for i, v in enumerate(results[:10])] )
self.FORMAT = "HTML"
elif msg == "!pongantitop":
def read_score(path, name):
if os.path.exists(path + f"pong-scoreboard/{name}"):
return open(path + f"pong-scoreboard/{name}").read()
else:
return None
if not os.path.exists(self.path + "pong-scoreboard/"):
os.mkdir(self.path + "pong-scoreboard/")
results = []
for name in os.listdir(self.path + "pong-scoreboard/"):
data = [i for i in read_score(self.path, name).split("\n") if i != ""]
data.sort(key = lambda x: float(x.split()[6]))
results.append(data[-1].split())
print(results)
results.sort(key = lambda x: float(x[6]))
results = results[::-1]
self.RESPONSE = "<u>Pong antitop</u>:\n" + "\n".join( [f"{i+1}. {v[3]} {v[4]} ({v[5]}) - {v[6]}" for i, v in enumerate(results[:10])] )
self.FORMAT = "HTML"

View File

@@ -0,0 +1,6 @@
{
"start_on_boot": true,
"alias": "pingtools",
"version": 1,
"index_file": "index.py"
}

133
modules/tag-sub/index.py Normal file
View File

@@ -0,0 +1,133 @@
import os
import json
def setup():
for i in ["sub-storage"]:
if not os.path.exists(module_path + i):
os.mkdir(module_path + i)
def load_group(group_name):
if os.path.exists(module_path + f"sub-storage/{group_name}.json"):
return json.loads(open(module_path + f"sub-storage/{group_name}.json").read())
else:
return {"hard": [], "soft": []}
def save_group(group_name, existing_group):
with open(module_path + f"sub-storage/{group_name}.json", "w") as f:
f.write(json.dumps(existing_group))
def sub_to_group(group_name, tag, persistence = "soft"):
for i in ["..", "/", "\\"]:
if i in group_name:
return False
existing_group = load_group(group_name)
if tag not in existing_group[persistence]:
existing_group[persistence].append(tag)
save_group(group_name, existing_group)
return True
def unsub_from_group(group_name, tag, persistence = "soft"):
for i in ["..", "/", "\\"]:
if i in group_name:
return False
existing_group = load_group(group_name)
if tag in existing_group[persistence]:
existing_group[persistence].remove(tag)
save_group(group_name, existing_group)
return True
def get_group(cmd):
if len(cmd) == 1:
return "default"
else:
return cmd[1]
def clear_subs_in(group_name, category = "soft"):
existing_group = load_group(group_name)
existing_group[category] = []
save_group(group_name, existing_group)
module_path = ""
setup()
def process(message, path):
global module_path
module_path = path
if not message.text[0] == "!":
return "", None
msg = message.text.lower()
cmd = [i for i in msg[1:].split(" ", 2) if i]
chosen_group_name = get_group(cmd)
if cmd[0] == "sub":
if message.from_user.username:
if sub_to_group(chosen_group_name, message.from_user.username):
return f"Subscribed {message.from_user.username} to the {chosen_group_name} event", None
else:
return f"Failed to subscribe {message.from_user.username} to the \"{chosen_group_name}\" event (event name violation)", None
else:
return f"Failed to subscribe {message.from_user.id} to the \"{chosen_group_name}\" event (no username available)", None
if cmd[0] == "hardsub":
if message.from_user.username:
if sub_to_group(chosen_group_name, message.from_user.username, persistence = "hard"):
return f"Hard-subscribed {message.from_user.username} to the {chosen_group_name} event", None
else:
return f"Failed to hard-subscribe {message.from_user.username} to the \"{chosen_group_name}\" event (event name violation)", None
else:
return f"Failed to hard-subscribe {message.from_user.id} to the \"{chosen_group_name}\" event (no username available)", None
elif cmd[0] == "unsub":
if message.from_user.username:
if unsub_from_group(chosen_group_name, message.from_user.username):
return f"Unsubscribed {message.from_user.username} from the {chosen_group_name} event", None
else:
return f"Failed to unsubscribe {message.from_user.username} from the \"{chosen_group_name}\" event (event name violation)", None
else:
return f"Failed to unsubscribe {message.from_user.id} from the \"{chosen_group_name}\" event (no username available)", None
elif cmd[0] == "hardunsub":
if message.from_user.username:
if unsub_from_group(chosen_group_name, message.from_user.username, persistence = "hard"):
return f"Unsubscribed {message.from_user.username} from the {chosen_group_name} event", None
else:
return f"Failed to unsubscribe {message.from_user.username} from the \"{chosen_group_name}\" event (event name violation)", None
else:
return f"Failed to unsubscribe {message.from_user.id} from the \"{chosen_group_name}\" event (no username available)", None
elif cmd[0] == "call":
group_data = load_group(chosen_group_name)
tags = set()
for subs in group_data.values():
tags |= set([f"@{i}" for i in subs if i])
ping_string = " ".join(tags)
clear_subs_in(chosen_group_name)
if len(cmd) == 3:
return f"{message.from_user.username} calls \"{chosen_group_name}\": {cmd[2]}\n{ping_string}", None
else:
return f"{message.from_user.username} calls \"{chosen_group_name}\"\n{ping_string}", None
elif cmd[0] == "sublist":
group_data = load_group(chosen_group_name)
responce = f"Subscribers for group \"{chosen_group_name}\":\n"
for name, subs in group_data.items():
responce += f"{name}: {', '.join(subs)}\n"
return responce, None

View File

@@ -0,0 +1,6 @@
{
"start_on_boot": true,
"alias": "tag-sub",
"version": 2,
"index_file": "index.py"
}

View File

@@ -22,7 +22,7 @@ if (command[0] in self.aliases) and (1 <= command_length <= 3):
"target": target, "target": target,
"format": "text"} "format": "text"}
res = requests.post("http://127.0.0.1:5000/translate", data = data) res = requests.post("http://10.1.1.1:5010/translate", data = data)
result = json.loads(res.text) result = json.loads(res.text)
if source == "auto": if source == "auto":

View File

@@ -4,8 +4,14 @@ command_length = len(command)
def escaped_string(unescaped_string): def escaped_string(unescaped_string):
result_string = str(unescaped_string) result_string = str(unescaped_string)
for i in ["<", ">"]: escaping_rules = {
result_string = result_string.replace(i, f"\\{i}") "<": "&lt;",
">": "&gt;",
'"': "&quot;"
}
for i in escaping_rules.items():
result_string = result_string.replace(i[0], i[1])
return result_string return result_string
@@ -31,7 +37,7 @@ if (command[0] in self.aliases) and (1 <= command_length <= 3):
f"{', '.join(list(models.keys()))}" f"{', '.join(list(models.keys()))}"
else: else:
for i in models[chosen_model]: for i in models[chosen_model]:
decoded_text = decoded_text.replace(i[0], i[1]) decoded_text = decoded_text.replace(i[0].lower(), i[1].lower())
decoded_text = decoded_text.replace(i[0].capitalize(), i[1].capitalize()) decoded_text = decoded_text.replace(i[0].capitalize(), i[1].capitalize())
decoded_text = decoded_text.replace(i[0].upper(), i[1].upper()) decoded_text = decoded_text.replace(i[0].upper(), i[1].upper())

View File

@@ -75,5 +75,57 @@
["$", ";"], ["$", ";"],
["^", ":"], ["^", ":"],
["&", "?"] ["&", "?"]
],
"en-men": [
["a", "4"],
["b", "8"],
["c", "("],
["d", "|)"],
["e", "3"],
["f", "|="],
["g", "6"],
["h", "#"],
["i", "!"],
["j", "|_"],
["k", "|<"],
["l", "1"],
["m", "//N"],
["o", "0"],
["p", "|D"],
["q", "(,)"],
["r", "|2"],
["s", "5"],
["t", "7"],
["u", "|_|"],
["w", "//"],
["v", "/"],
["x", "><"],
["y", "`/"]
],
"men-en": [
["4", "a"],
["8", "b"],
["(,)", "q"],
["(", "c"],
["|)", "d"],
["3", "e"],
["|=", "f"],
["6", "g"],
["#", "h"],
["!", "i"],
["|_|", "u"],
["|_", "j"],
["|<", "k"],
["1", "l"],
["//N", "m"],
["0", "o"],
["|D", "p"],
["|2", "r"],
["5", "s"],
["7", "t"],
["//", "w"],
["`/", "y"],
["/", "v"],
["><", "x"]
] ]
} }

View File

@@ -0,0 +1,3 @@
def process(message, path):
if "силк" in message.text.lower() or "лінк" in message.text.lower():
return "*посилання 🌚", None

View File

@@ -0,0 +1,6 @@
{
"start_on_boot": true,
"alias": "troll-spelling-corrector",
"version": 2,
"index_file": "index.py"
}