From c9895a4bedc891b127ea6130205d475d57da2e87 Mon Sep 17 00:00:00 2001 From: octospacc Date: Wed, 7 Aug 2024 02:27:17 +0200 Subject: [PATCH] Cleanup Locale system, remove legacy Locale API, add command help handling, misc --- LibWinDog/Platforms/Mastodon/Mastodon.py | 2 +- LibWinDog/Platforms/Matrix/Matrix.py | 6 +- LibWinDog/Platforms/Telegram/Telegram.py | 23 ++- LibWinDog/Platforms/Web/Web.py | 2 +- .../Platforms/Web/multithread_http_server.py | 0 LibWinDog/Types.py | 19 ++- Locale/en.json | 42 ------ Locale/it.json | 139 ------------------ ModWinDog/Base/Base.py | 6 +- ModWinDog/Base/Base.yaml | 0 ModWinDog/Broadcast/Broadcast.py | 4 +- ModWinDog/Broadcast/Broadcast.yaml | 0 ModWinDog/Dumper/Dumper.py | 9 +- ModWinDog/Dumper/Dumper.yaml | 9 +- ModWinDog/Echo/Echo.py | 11 +- ModWinDog/GPT/GPT.py | 2 +- ModWinDog/GPT/GPT.yaml | 0 ModWinDog/Hashing/Hashing.py | 12 +- ModWinDog/Hashing/Hashing.yaml | 11 +- ModWinDog/Help/Help.py | 8 +- ModWinDog/Help/Help.yaml | 5 + ModWinDog/Internet/Internet.py | 8 +- ModWinDog/Internet/Internet.yaml | 0 ModWinDog/Multifun/Multifun.py | 29 ++-- ModWinDog/Multifun/Multifun.yaml | 116 +++++++++++++++ ModWinDog/Percenter/Percenter.py | 9 +- ModWinDog/Percenter/Percenter.yaml | 27 ++++ ModWinDog/Scrapers/Scrapers.py | 4 +- ModWinDog/Scrapers/Scrapers.yaml | 0 ModWinDog/Scripting/Scripting.py | 2 +- ModWinDog/Scripting/Scripting.yaml | 0 ModWinDog/Start/Start.py | 0 ModWinDog/Start/Start.yaml | 0 ModWinDog/System/System.py | 4 +- ModWinDog/System/System.yaml | 0 WinDog.py | 89 ++++++----- WinDog.yaml | 13 ++ 37 files changed, 314 insertions(+), 297 deletions(-) mode change 100644 => 100755 LibWinDog/Platforms/Web/multithread_http_server.py delete mode 100755 Locale/en.json delete mode 100755 Locale/it.json mode change 100644 => 100755 ModWinDog/Base/Base.yaml mode change 100644 => 100755 ModWinDog/Broadcast/Broadcast.yaml mode change 100644 => 100755 ModWinDog/GPT/GPT.yaml mode change 100644 => 100755 ModWinDog/Help/Help.yaml mode change 100644 => 100755 ModWinDog/Internet/Internet.yaml mode change 100644 => 100755 ModWinDog/Multifun/Multifun.yaml mode change 100644 => 100755 ModWinDog/Percenter/Percenter.yaml mode change 100644 => 100755 ModWinDog/Scrapers/Scrapers.yaml mode change 100644 => 100755 ModWinDog/Scripting/Scripting.yaml mode change 100644 => 100755 ModWinDog/Start/Start.py mode change 100644 => 100755 ModWinDog/Start/Start.yaml mode change 100644 => 100755 ModWinDog/System/System.py mode change 100644 => 100755 ModWinDog/System/System.yaml create mode 100644 WinDog.yaml diff --git a/LibWinDog/Platforms/Mastodon/Mastodon.py b/LibWinDog/Platforms/Mastodon/Mastodon.py index f27c63a..a1a0477 100755 --- a/LibWinDog/Platforms/Mastodon/Mastodon.py +++ b/LibWinDog/Platforms/Mastodon/Mastodon.py @@ -36,7 +36,7 @@ def MastodonMakeInputMessageData(status:dict) -> InputMessageData: while command_tokens[0].strip().startswith('@') or not command_tokens[0]: command_tokens.pop(0) data.command = ParseCommand(" ".join(command_tokens)) - data.user = SafeNamespace( + data.user = UserData( id = ("mastodon:" + strip_url_scheme(status["account"]["uri"])), name = status["account"]["display_name"], ) diff --git a/LibWinDog/Platforms/Matrix/Matrix.py b/LibWinDog/Platforms/Matrix/Matrix.py index 7851c87..d92b196 100755 --- a/LibWinDog/Platforms/Matrix/Matrix.py +++ b/LibWinDog/Platforms/Matrix/Matrix.py @@ -48,6 +48,7 @@ def MatrixMain() -> bool: await MatrixClient.sync(30000) # resync old messages first to "skip read ones" asyncio.ensure_future(queue_handler()) MatrixClient.add_event_callback(MatrixMessageHandler, nio.RoomMessage) + MatrixClient.add_event_callback(MatrixInviteHandler, nio.InviteEvent) await MatrixClient.sync_forever(timeout=30000) Thread(target=lambda:asyncio.run(client_main())).start() return True @@ -63,7 +64,7 @@ def MatrixMakeInputMessageData(room:nio.MatrixRoom, event:nio.RoomMessage) -> In id = f"matrix:{room.room_id}", name = room.display_name, ), - user = SafeNamespace( + user = UserData( id = f"matrix:{event.sender}", #name = , # TODO name must be get via a separate API request (and so maybe we should cache it) ), @@ -75,6 +76,9 @@ def MatrixMakeInputMessageData(room:nio.MatrixRoom, event:nio.RoomMessage) -> In data.user.settings = (GetUserSettings(data.user.id) or SafeNamespace()) return data +async def MatrixInviteHandler(room:nio.MatrixRoom, event:nio.InviteEvent) -> None: + await MatrixClient.join(room.room_id) + async def MatrixMessageHandler(room:nio.MatrixRoom, event:nio.RoomMessage) -> None: if MatrixUsername == event.sender: return # ignore messages that come from the bot itself diff --git a/LibWinDog/Platforms/Telegram/Telegram.py b/LibWinDog/Platforms/Telegram/Telegram.py index 0942a64..ba055e8 100755 --- a/LibWinDog/Platforms/Telegram/Telegram.py +++ b/LibWinDog/Platforms/Telegram/Telegram.py @@ -32,6 +32,13 @@ def TelegramMain() -> bool: #app.run_polling(allowed_updates=Update.ALL_TYPES) return True +def TelegramMakeUserData(user:telegram.User) -> UserData: + return UserData( + id = f"telegram:{user.id}", + tag = user.username, + name = user.first_name, + ) + def TelegramMakeInputMessageData(message:telegram.Message) -> InputMessageData: #if not message: # return None @@ -40,11 +47,7 @@ def TelegramMakeInputMessageData(message:telegram.Message) -> InputMessageData: datetime = int(time.mktime(message.date.timetuple())), text_plain = message.text, text_markdown = message.text_markdown_v2, - user = SafeNamespace( - id = f"telegram:{message.from_user.id}", - tag = message.from_user.username, - name = message.from_user.first_name, - ), + user = TelegramMakeUserData(message.from_user), room = SafeNamespace( id = f"telegram:{message.chat.id}", tag = message.chat.username, @@ -105,5 +108,13 @@ def TelegramLinker(data:InputMessageData) -> SafeNamespace: linked.message = f"https://t.me/c/{room_id}/{message_id}" return linked -RegisterPlatform(name="Telegram", main=TelegramMain, sender=TelegramSender, linker=TelegramLinker, event_class=telegram.Update, manager_class=(lambda:TelegramClient)) +RegisterPlatform( + name="Telegram", + main=TelegramMain, + sender=TelegramSender, + linker=TelegramLinker, + event_class=telegram.Update, + manager_class=(lambda:TelegramClient), + agent_info=(lambda:TelegramMakeUserData(TelegramClient.bot.get_me())), +) diff --git a/LibWinDog/Platforms/Web/Web.py b/LibWinDog/Platforms/Web/Web.py index 409daec..d5fcba4 100755 --- a/LibWinDog/Platforms/Web/Web.py +++ b/LibWinDog/Platforms/Web/Web.py @@ -70,7 +70,7 @@ def WebMakeInputMessageData(text:str, uuid:str) -> InputMessageData: room = SafeNamespace( id = f"web:{uuid}", ), - user = SafeNamespace( + user = UserData( settings = SafeNamespace(), ), ) diff --git a/LibWinDog/Platforms/Web/multithread_http_server.py b/LibWinDog/Platforms/Web/multithread_http_server.py old mode 100644 new mode 100755 diff --git a/LibWinDog/Types.py b/LibWinDog/Types.py index 273cbb8..83e84ce 100755 --- a/LibWinDog/Types.py +++ b/LibWinDog/Types.py @@ -5,18 +5,29 @@ from types import SimpleNamespace -class SafeNamespace(SimpleNamespace): - def __getattribute__(self, value): +class DictNamespace(SimpleNamespace): + def __iter__(self): + return self.__dict__.__iter__() + def __getitem__(self, key): + return self.__getattribute__(key) + def __setitem__(self, key, value): + return self.__setattr__(key, value) + +class SafeNamespace(DictNamespace): + def __getattribute__(self, key): try: - return super().__getattribute__(value) + return super().__getattribute__(key) except AttributeError: return None -# we just use these for type hinting: +# we just use these for type hinting and clearer code: class EventContext(SafeNamespace): pass +class UserData(SafeNamespace): + pass + class MessageData(SafeNamespace): pass diff --git a/Locale/en.json b/Locale/en.json deleted file mode 100755 index e578972..0000000 --- a/Locale/en.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "help": [ - "*There's no one around to help (yet).*" - ], - "wish": { - "empty": [ - "*You wished for nothing! ✨*\n\n_Nothing happens..._" - ], - "done": [ - "*Your wish has been cast! ✨*\n\n_Chance of success: *{Percent}%*._" - ] - }, - "hug": { - "empty": [ - "*Hug? You didn't reply to anyone...*" - ], - "bot": [], - "self": [], - "others": [ - "*{0} hugs {1} tightly... :3*" - ] - }, - "level": { - "empty": [ - "Check what's your level of something.\n\n*Usage*: {Cmd} <Thing>." - ], - "done": [ - "_Your level of *{Thing}* is... *{Percent}%*._" - ] - }, - "hash": { - "usage": [ - "*Usage*: {0} <Algorithm> <Text to Hash>.\n\n*Available algorithms*: {1}." - ] - }, - "eval": [ - "This feature is not implemented [Security Issue]." - ], - "time": [ - "Local time: *{0}*." - ] -} diff --git a/Locale/it.json b/Locale/it.json deleted file mode 100755 index a26ff1e..0000000 --- a/Locale/it.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "help": [ - "*Non c'è nessuno qui ad aiutarti (per ora).*" - ], - "wish": { - "empty": [ - "*Non hai desiderato nulla! ✨*\n\n_Non succede niente..._" - ], - "done": [ - "*Il tuo desiderio è stato espresso! ✨*\n\n_Probabilità che si avveri: *{Percent}%*._" - ] - }, - "level": { - "empty": [ - "Controlla il tuo livello di qualcosa.\n\n*Uso*: {Cmd} <Cosa>." - ], - "done": [ - "_Il tuo livello di *{Thing}* è... *{Percent}%*._" - ] - }, - "hug": { - "empty": [ - "*Chi vuoi abbracciare? Rispondi a qualcuno.*", - "*Un abbraccio a chi? Non hai risposto a nessuno...*" - ], - "bot": [ - "*...Grazie uwu <3*", - "*Non puoi abbracciarmi ☁️ :/*" - ], - "self": [ - "*{0} allunga le braccia attorno a sè, si stringe forte, e si sente ora meno triste.*" - ], - "others": [ - "*{0} abbraccia {1} forte forte... :3*", - "*{0} ti ha dato un abbraccio!*" - ] - }, - "pat": { - "empty": [ - "*Chi vuoi accarezzare? Rispondi a qualcuno.*", - "*Una carezza a chi? Non hai risposto a nessuno...*" - ], - "bot": [ - "*Rawrrr xDDdd*", - "*Come vuoi accarezzarmi? Vivo nel cloud... :(*" - ], - "self": [ - "*{0} allunga goffamente la mano dietro di sè per accarezzarsi in testa 🙃*" - ], - "others": [ - "*{0} fa patpat a {1} ^.^*", - "*{0} ti accarezza con dolcezza.*" - ] - }, - "poke": { - "empty": [ - "*Chi vuoi punzecchiare? Rispondi a qualcuno.*", - "*Punzecchiare chi? Non hai risposto a nessuno...*" - ], - "bot": [ - "*>_< perché?*", - "*heh ☁️😌*" - ], - "self": [ - "*{0} si punzecchia :o*" - ], - "others": [ - "*{0} punzecchia {1} 👀*", - "*{0} ti punzecchia con impertinenza 👉*" - ] - }, - "cuddle": { - "empty": [ - "*A chi vuoi fare le coccole? Rispondi a qualcuno.*", - "*Le coccole? A chi? Non hai risposto a nessuno...*" - ], - "bot": [ - "*Aww.. grazie :3*", - "*Vorrei anche io le coccole ma... ☁️😔*" - ], - "self": [ - "*{0} si stende sul letto e prova a farsi le coccole, per sentirsi meglio.*" - ], - "others": [ - "*{0} fa le coccole a {1} 🥰*", - "*{0} ti fa le coccole! 🤗️*" - ] - }, - "floor": { - "empty": [ - "*Il pavimento?*", - "*Il pavimentowo?*", - "*Vuoi mettere qualcuno sul pavimento? Rispondi ad un suo messaggio.*" - ], - "bot": [ - "*Sono già sul pavimento.. sono sempre sul pavimento.*" - ], - "self": [ - "*{0} si mette sul pavimento.*" - ], - "others": [ - "*{0} solleva {1} dal pavimento.*", - "*{0} ti prende in braccio e ti appoggia con delicatezza sul pavimento.*" - ] - }, - "hands": { - "empty": [ - "*Le t.me/manineuwu? 😳️*", - "*A chi vuoi dare le manine? Rispondi a qualcuno.*" - ], - "bot": [ - "*Io non le ho le manine,.,. 😭️*" - ], - "self": [ - "*{0} fa handholding in solitudine... 😶️*", - "*{0} non ha nessuno a cui dare la mano 😐️*" - ], - "others": [ - "*{0} prende le mani di {1} 😳️❤️*", - "*{0} vuole darti la sua manina 🥺👉️👈️*" - ] - }, - "sessocto": { - "empty": [ - "*Sessocto?!?! 😳️*", - "*Vuoi fare sessocto con qualcuno? Rispondi ad un suo messaggio.*" - ], - "bot": [ - "*Vorrei anche io sessocto ma non ho un corpo... 😥️*" - ], - "self": [ - "*{0} fa sessocto in singleplayer 😳️*" - ], - "others": [ - "*{0} vuole fare sessocto con te... 👀️*", - "*{0} e {1} fanno sessocto insieme 💑️*" - ] - } -} diff --git a/ModWinDog/Base/Base.py b/ModWinDog/Base/Base.py index 04d2e38..87b7007 100755 --- a/ModWinDog/Base/Base.py +++ b/ModWinDog/Base/Base.py @@ -38,12 +38,12 @@ def cPing(context:EventContext, data:InputMessageData) -> None: # CharEscape(choice(Locale.__('time')).format(time.ctime().replace(' ', ' ')), 'MARKDOWN_SPEECH'), # reply_to_message_id=update.message.message_id) -def cEval(context:EventContext, data:InputMessageData) -> None: - SendMessage(context, {"Text": choice(Locale.__('eval'))}) +#def cEval(context:EventContext, data:InputMessageData) -> None: +# SendMessage(context, {"Text": choice(Locale.__('eval'))}) RegisterModule(name="Base", endpoints=[ SafeNamespace(names=["source"], handler=cSource), - SafeNamespace(names=["config"], handler=cConfig, arguments={ + SafeNamespace(names=["config"], handler=cConfig, body=False, arguments={ "get": True, }), #SafeNamespace(names=["gdpr"], summary="Operations for european citizens regarding your personal data.", handler=cGdpr), diff --git a/ModWinDog/Base/Base.yaml b/ModWinDog/Base/Base.yaml old mode 100644 new mode 100755 diff --git a/ModWinDog/Broadcast/Broadcast.py b/ModWinDog/Broadcast/Broadcast.py index 7789458..45fa943 100755 --- a/ModWinDog/Broadcast/Broadcast.py +++ b/ModWinDog/Broadcast/Broadcast.py @@ -5,7 +5,7 @@ def cBroadcast(context:EventContext, data:InputMessageData) -> None: if (data.user.id not in AdminIds) and (data.user.tag not in AdminIds): - return SendMessage(context, {"Text": choice(Locale.__('eval'))}) + return SendMessage(context, {"Text": "Permission denied."}) destination = data.command.arguments["destination"] text = data.command.body if not (destination and text): @@ -14,7 +14,7 @@ def cBroadcast(context:EventContext, data:InputMessageData) -> None: SendMessage(context, OutputMessageData(text_plain="Executed.")) RegisterModule(name="Broadcast", endpoints=[ - SafeNamespace(names=["broadcast"], handler=cBroadcast, arguments={ + SafeNamespace(names=["broadcast"], handler=cBroadcast, body=True, arguments={ "destination": True, }), ]) diff --git a/ModWinDog/Broadcast/Broadcast.yaml b/ModWinDog/Broadcast/Broadcast.yaml old mode 100644 new mode 100755 diff --git a/ModWinDog/Dumper/Dumper.py b/ModWinDog/Dumper/Dumper.py index 9566045..60b5e26 100755 --- a/ModWinDog/Dumper/Dumper.py +++ b/ModWinDog/Dumper/Dumper.py @@ -6,11 +6,12 @@ from json import dumps as json_dumps def cDump(context:EventContext, data:InputMessageData): - if not (message := ObjGet(data, "quoted")): - pass # TODO send error message - SendMessage(context, {"TextPlain": json_dumps(message, default=(lambda obj: obj.__dict__), indent=" ")}) + SendMessage(context, { + "text_html": (f'
{json_dumps(message, default=(lambda obj: obj.__dict__), indent="  ")}
' + if (message := ObjGet(data, "quoted")) + else context.endpoint.help_text(data.user.settings.language))}) RegisterModule(name="Dumper", group="Geek", endpoints=[ - SafeNamespace(names=["dump"], handler=cDump), + SafeNamespace(names=["dump"], handler=cDump, quoted=True), ]) diff --git a/ModWinDog/Dumper/Dumper.yaml b/ModWinDog/Dumper/Dumper.yaml index eec9361..3ae4d81 100755 --- a/ModWinDog/Dumper/Dumper.yaml +++ b/ModWinDog/Dumper/Dumper.yaml @@ -1,6 +1,9 @@ summary: en: Functions for dumping of various data. -dump: - summary: - en: Dumps the data for a quoted message and returns the JSON representation. +endpoints: + dump: + summary: + en: Dumps the data for a quoted message and returns the JSON representation. + body: + en: Target message diff --git a/ModWinDog/Echo/Echo.py b/ModWinDog/Echo/Echo.py index b9d0c12..b4c1999 100755 --- a/ModWinDog/Echo/Echo.py +++ b/ModWinDog/Echo/Echo.py @@ -5,21 +5,22 @@ def cEcho(context:EventContext, data:InputMessageData) -> None: if not (text := ObjGet(data, "command.body")): - return SendMessage(context, {"text_html": context.endpoint.get_string("empty", data.user.settings.language)}) + return SendMessage(context, { + "text_html": context.endpoint.get_string("empty", data.user.settings.language)}) prefix = f'🗣️ ' - #prefix = f"[🗣️]({context.linker(data).message}) " - if len(data.command.tokens) == 2: + if len(data.command.tokens) == 2: # text is a single word nonascii = True for char in data.command.tokens[1]: if ord(char) < 256: nonascii = False break if nonascii: - # text is not ascii, probably an emoji (altough not necessarily), so just pass as is (useful for Telegram emojis) + # word is not ascii, probably an emoji (altough not necessarily) + # so just pass it as is (useful for Telegram emojis) prefix = '' SendMessage(context, {"text_html": (prefix + html_escape(text))}) RegisterModule(name="Echo", endpoints=[ - SafeNamespace(names=["echo"], handler=cEcho), + SafeNamespace(names=["echo"], handler=cEcho, body=True), ]) diff --git a/ModWinDog/GPT/GPT.py b/ModWinDog/GPT/GPT.py index 108cead..14e3d7f 100755 --- a/ModWinDog/GPT/GPT.py +++ b/ModWinDog/GPT/GPT.py @@ -18,6 +18,6 @@ def cGpt(context:EventContext, data:InputMessageData) -> None: return SendMessage(context, {"TextPlain": f"[🤖️ GPT]\n\n{output}"}) RegisterModule(name="GPT", endpoints=[ - SafeNamespace(names=["gpt", "chatgpt"], handler=cGpt), + SafeNamespace(names=["gpt", "chatgpt"], handler=cGpt, body=True), ]) diff --git a/ModWinDog/GPT/GPT.yaml b/ModWinDog/GPT/GPT.yaml old mode 100644 new mode 100755 diff --git a/ModWinDog/Hashing/Hashing.py b/ModWinDog/Hashing/Hashing.py index 6429894..251f6a5 100755 --- a/ModWinDog/Hashing/Hashing.py +++ b/ModWinDog/Hashing/Hashing.py @@ -6,15 +6,17 @@ import hashlib def cHash(context:EventContext, data:InputMessageData): - text_input = ObjGet(data, "command.body") - algorithm = ObjGet(data, "command.arguments.algorithm") + text_input = (data.command.body or (data.quoted and data.quoted.text_plain)) + algorithm = data.command.arguments.algorithm + language = data.user.settings.language if not (text_input and (algorithm in hashlib.algorithms_available)): - return SendMessage(context, {"Text": choice(Locale.__('hash.usage')).format(data.command.tokens[0], hashlib.algorithms_available)}) + return SendMessage(context, { + "text_html": f'{context.endpoint.help_text(language)}\n\n{context.endpoint.get_string("algorithms", language)}: {hashlib.algorithms_available}'}) hashed = hashlib.new(algorithm, text_input.encode()).hexdigest() - return SendMessage(context, OutputMessageData(text_plain=hashed, text_html=f"
{hashed}
")) + return SendMessage(context, {"text_html": f"
{hashed}
"}) RegisterModule(name="Hashing", group="Geek", endpoints=[ - SafeNamespace(names=["hash"], handler=cHash, arguments={ + SafeNamespace(names=["hash"], handler=cHash, body=False, quoted=False, arguments={ "algorithm": True, }), ]) diff --git a/ModWinDog/Hashing/Hashing.yaml b/ModWinDog/Hashing/Hashing.yaml index 74ea271..3d0af1b 100755 --- a/ModWinDog/Hashing/Hashing.yaml +++ b/ModWinDog/Hashing/Hashing.yaml @@ -4,12 +4,15 @@ summary: endpoints: hash: summary: - en: Responds with the hash-sum of the received message. + en: Responds with the hash-sum of the included or quoted text. + algorithms: + en: Available algorithms + it: Algoritmi disponibili arguments: algorithm: en: Algorithm it: Algoritmo - body: - en: Text to hash - it: Testo da hashare + body: + en: Text to hash + it: Testo da hashare diff --git a/ModWinDog/Help/Help.py b/ModWinDog/Help/Help.py index 5cdd914..c4c08d7 100755 --- a/ModWinDog/Help/Help.py +++ b/ModWinDog/Help/Help.py @@ -5,16 +5,16 @@ # TODO: implement /help feature def cHelp(context:EventContext, data:InputMessageData) -> None: - module_list = '' + text = (context.endpoint.get_string(lang=data.user.settings.language) or '') language = data.user.settings.language for module in Modules: summary = Modules[module].get_string("summary", language) endpoints = Modules[module].endpoints - module_list += (f"\n\n{module}" + (f": {summary}" if summary else '')) + text += (f"\n\n{module}" + (f": {summary}" if summary else '')) for endpoint in endpoints: summary = Modules[module].get_string(f"endpoints.{endpoint.names[0]}.summary", language) - module_list += (f"\n* /{', /'.join(endpoint.names)}" + (f": {summary}" if summary else '')) - SendMessage(context, OutputMessageData(text=module_list)) + text += (f"\n* /{', /'.join(endpoint.names)}" + (f": {summary}" if summary else '')) + SendMessage(context, {"text_html": text}) RegisterModule(name="Help", group="Basic", endpoints=[ SafeNamespace(names=["help"], handler=cHelp), diff --git a/ModWinDog/Help/Help.yaml b/ModWinDog/Help/Help.yaml old mode 100644 new mode 100755 index e0bcc0a..f8f6263 --- a/ModWinDog/Help/Help.yaml +++ b/ModWinDog/Help/Help.yaml @@ -2,4 +2,9 @@ endpoints: help: summary: en: Provides help for the bot. For now, it just lists the commands. + help: + en: > + There's no one around to help. However, you can have the commands list: + it: > + Non c'è nessuno qui ad aiutarti. Tuttavia, puoi avere la lista dei comandi: diff --git a/ModWinDog/Internet/Internet.py b/ModWinDog/Internet/Internet.py index d2c2870..4149a42 100755 --- a/ModWinDog/Internet/Internet.py +++ b/ModWinDog/Internet/Internet.py @@ -137,13 +137,13 @@ def cSafebooru(context:EventContext, data:InputMessageData) -> None: raise RegisterModule(name="Internet", endpoints=[ - SafeNamespace(names=["embedded"], handler=cEmbedded), - SafeNamespace(names=["web"], handler=cWeb), - SafeNamespace(names=["translate"], handler=cTranslate, arguments={ + SafeNamespace(names=["embedded"], handler=cEmbedded, body=False, quoted=False), + SafeNamespace(names=["web"], handler=cWeb, body=True), + SafeNamespace(names=["translate"], handler=cTranslate, body=False, quoted=False, arguments={ "language_to": True, "language_from": False, }), #SafeNamespace(names=["unsplash"], summary="Sends a picture sourced from Unsplash.", handler=cUnsplash), - SafeNamespace(names=["safebooru"], handler=cSafebooru), + SafeNamespace(names=["safebooru"], handler=cSafebooru, body=False), ]) diff --git a/ModWinDog/Internet/Internet.yaml b/ModWinDog/Internet/Internet.yaml old mode 100644 new mode 100755 diff --git a/ModWinDog/Multifun/Multifun.py b/ModWinDog/Multifun/Multifun.py index 45bb8f5..d5e9415 100755 --- a/ModWinDog/Multifun/Multifun.py +++ b/ModWinDog/Multifun/Multifun.py @@ -4,23 +4,22 @@ # ================================== # def mMultifun(context:EventContext, data:InputMessageData) -> None: - cmdkey = data.command.name - replyToId = None + reply_to = None + fun_strings = {} + for key in ("empty", "bot", "self", "others"): + fun_strings[key] = context.endpoint.get_string(key, data.user.settings.language) if data.quoted: - replyFromUid = data.quoted.user.id - # TODO work on all platforms for the bot id - if replyFromUid.split(':')[1] == TelegramToken.split(':')[0] and 'bot' in Locale.__(cmdkey): - Text = choice(Locale.__(f'{cmdkey}.bot')) - elif replyFromUid == data.user.id and 'self' in Locale.__(cmdkey): - Text = choice(Locale.__(f'{cmdkey}.self')).format(data.user.name) - else: - if 'others' in Locale.__(cmdkey): - Text = choice(Locale.__(f'{cmdkey}.others')).format(data.user.name, data.quoted.user.name) - replyToId = data.quoted.message_id + if fun_strings["bot"] and (data.quoted.user.id == Platforms[context.platform].agent_info.id): + text = choice(fun_strings["bot"]) + elif fun_strings["self"] and (data.quoted.user.id == data.user.id): + text = choice(fun_strings["self"]).format(data.user.name) + elif fun_strings["others"]: + text = choice(fun_strings["others"]).format(data.user.name, data.quoted.user.name) + reply_to = data.quoted.message_id else: - if 'empty' in Locale.__(cmdkey): - Text = choice(Locale.__(f'{cmdkey}.empty')) - SendMessage(context, {"Text": Text, "ReplyTo": replyToId}) + if fun_strings["empty"]: + text = choice(fun_strings["empty"]) + SendMessage(context, {"text_html": text, "ReplyTo": reply_to}) RegisterModule(name="Multifun", endpoints=[ SafeNamespace(names=["hug", "pat", "poke", "cuddle", "hands", "floor", "sessocto"], handler=mMultifun), diff --git a/ModWinDog/Multifun/Multifun.yaml b/ModWinDog/Multifun/Multifun.yaml old mode 100644 new mode 100755 index d71fa47..7a709e1 --- a/ModWinDog/Multifun/Multifun.yaml +++ b/ModWinDog/Multifun/Multifun.yaml @@ -1,3 +1,119 @@ summary: en: Provides fun trough preprogrammed text-based toys. +endpoints: + hug: + empty: + en: + - Hug? You haven't replied to anyone... + it: + - Chi vuoi abbracciare? Rispondi a qualcuno. + - Un abbraccio a chi? Non hai risposto a nessuno... + bot: + it: + - ...Grazie uwu <3 + - Non puoi abbracciarmi ☁️ :/ + self: + it: + - {0} allunga le braccia attorno a sè, si stringe forte, e si sente ora meno triste. + others: + en: + - {0} hugs {1} tightly... :3 + it: + - {0} abbraccia {1} forte forte... :3 + - {0} ti ha dato un abbraccio! + pat: + empty: + it: + - Chi vuoi accarezzare? Rispondi a qualcuno. + - Una carezza a chi? Non hai risposto a nessuno... + bot: + it: + - Rawrrr xDDdd + - Come vuoi accarezzarmi? Vivo nel cloud... :( + self: + it: + - {0} allunga goffamente la mano dietro di sè per accarezzarsi in testa 🙃 + others: + it: + - {0} fa patpat a {1} ^.^ + - {0} ti accarezza con dolcezza. + poke: + empty: + it: + - Chi vuoi punzecchiare? Rispondi a qualcuno. + - Punzecchiare chi? Non hai risposto a nessuno... + bot: + it: + - >_< perché? + - heh ☁️😌 + self: + it: + - {0} si punzecchia :o + others: + it: + - {0} punzecchia {1} 👀* + - {0} ti punzecchia con impertinenza 👉 + cuddle: + empty: + it: + - A chi vuoi fare le coccole? Rispondi a qualcuno. + - Le coccole? A chi? Non hai risposto a nessuno... + bot: + it: + - Aww.. grazie :3 + - Vorrei anche io le coccole ma... ☁️😔 + self: + it: + - {0} si stende sul letto e prova a farsi le coccole, per sentirsi meglio. + others: + it: + - {0} fa le coccole a {1} 🥰 + - {0} ti fa le coccole! 🤗️ + floor: + empty: + it: + - Il pavimento? + - Il pavimentowo? + - Vuoi mettere qualcuno sul pavimento? Rispondi ad un suo messaggio. + bot: + it: + - Sono già sul pavimento.. sono sempre sul pavimento. + self: + it: + - {0} si mette sul pavimento. + others: + it: + - {0} solleva {1} dal pavimento. + - {0} ti prende in braccio e ti appoggia con delicatezza sul pavimento. + hands: + empty: + it: + - Le t.me/manineuwu? 😳️ + - A chi vuoi dare le manine? Rispondi a qualcuno. + bot: + it: + - Io non le ho le manine,.,. 😭️ + self: + it: + - {0} fa handholding in solitudine... 😶️ + - {0} non ha nessuno a cui dare la mano 😐️ + others: + it: + - {0} prende le mani di {1} 😳️❤️ + - {0} vuole darti la sua manina 🥺👉️👈️ + sessocto: + empty: + it: + - Sessocto?!?! 😳️ + - Vuoi fare sessocto con qualcuno? Rispondi ad un suo messaggio. + bot: + it: + - Vorrei anche io sessocto ma non ho un corpo... 😥️ + self: + it: + - {0} fa sessocto in singleplayer 😳️ + others: + it: + - {0} vuole fare sessocto con te... 👀️ + - {0} e {1} fanno sessocto insieme 💑️ diff --git a/ModWinDog/Percenter/Percenter.py b/ModWinDog/Percenter/Percenter.py index 2e50308..25f0db8 100755 --- a/ModWinDog/Percenter/Percenter.py +++ b/ModWinDog/Percenter/Percenter.py @@ -4,10 +4,13 @@ # ==================================== # def mPercenter(context:EventContext, data:InputMessageData) -> None: - SendMessage(context, {"Text": choice(Locale.__(f'{data.command.name}.{"done" if data.command.body else "empty"}')).format( - Cmd=data.command.tokens[0], Percent=RandPercent(), Thing=data.command.body)}) + SendMessage(context, {"text_html": (context.endpoint.get_string( + ("done" if data.command.body else "empty"), + data.user.settings.language + ) or context.endpoint.help_text(data.user.settings.language) + ).format(RandPercent(), data.command.body)}) RegisterModule(name="Percenter", endpoints=[ - SafeNamespace(names=["wish", "level"], handler=mPercenter), + SafeNamespace(names=["wish", "level"], handler=mPercenter, body=True), ]) diff --git a/ModWinDog/Percenter/Percenter.yaml b/ModWinDog/Percenter/Percenter.yaml old mode 100644 new mode 100755 index 7dd2f46..c3e40f9 --- a/ModWinDog/Percenter/Percenter.yaml +++ b/ModWinDog/Percenter/Percenter.yaml @@ -1,3 +1,30 @@ summary: en: Provides fun trough percentage-based toys. +endpoints: + wish: + empty: + en: | + You wished for nothing! ✨ + + Nothing happens... + it: | + Non hai desiderato nulla! ✨ + + Non succede niente... + done: + en: | + Your wish has been cast! ✨ + + Chance of success: {0}%. + it: | + Il tuo desiderio è stato espresso! ✨ + + Probabilità che si avveri: {0}%. + level: + summary: + en: Check what's your level of something. + it: Controlla il tuo livello di qualcosa. + done: + en: Your level of {1} is... {0}%. + it: Il tuo livello di {1} è... {0}%. diff --git a/ModWinDog/Scrapers/Scrapers.py b/ModWinDog/Scrapers/Scrapers.py index a246512..461b935 100755 --- a/ModWinDog/Scrapers/Scrapers.py +++ b/ModWinDog/Scrapers/Scrapers.py @@ -119,7 +119,7 @@ def cCraiyonSelenium(context:EventContext, data:InputMessageData) -> None: closeSelenium(driver_index, driver) RegisterModule(name="Scrapers", endpoints=[ - SafeNamespace(names=["dalle"], handler=cDalleSelenium), - SafeNamespace(names=["craiyon", "crayion"], handler=cCraiyonSelenium), + SafeNamespace(names=["dalle"], handler=cDalleSelenium, body=True), + SafeNamespace(names=["craiyon", "crayion"], handler=cCraiyonSelenium, body=True), ]) diff --git a/ModWinDog/Scrapers/Scrapers.yaml b/ModWinDog/Scrapers/Scrapers.yaml old mode 100644 new mode 100755 diff --git a/ModWinDog/Scripting/Scripting.py b/ModWinDog/Scripting/Scripting.py index deecba6..dccd408 100755 --- a/ModWinDog/Scripting/Scripting.py +++ b/ModWinDog/Scripting/Scripting.py @@ -53,6 +53,6 @@ end)()""")) SendMessage(context, {"TextPlain": textOutput}) RegisterModule(name="Scripting", group="Geek", endpoints=[ - SafeNamespace(names=["lua"], handler=cLua), + SafeNamespace(names=["lua"], handler=cLua, body=False, quoted=False), ]) diff --git a/ModWinDog/Scripting/Scripting.yaml b/ModWinDog/Scripting/Scripting.yaml old mode 100644 new mode 100755 diff --git a/ModWinDog/Start/Start.py b/ModWinDog/Start/Start.py old mode 100644 new mode 100755 diff --git a/ModWinDog/Start/Start.yaml b/ModWinDog/Start/Start.yaml old mode 100644 new mode 100755 diff --git a/ModWinDog/System/System.py b/ModWinDog/System/System.py old mode 100644 new mode 100755 index d8d43ae..d114a04 --- a/ModWinDog/System/System.py +++ b/ModWinDog/System/System.py @@ -15,7 +15,7 @@ from re import compile as re_compile def cExec(context:EventContext, data:InputMessageData) -> None: if not (len(data.command.tokens) >= 2 and data.command.tokens[1].lower() in ExecAllowed): - return SendMessage(context, {"Text": choice(Locale.__('eval'))}) + return SendMessage(context, {"Text": "This feature is not implemented [Security Issue]."}) command = data.command.tokens[1].lower() output = subprocess.run( ("sh", "-c", f"export PATH=$PATH:/usr/games; {command}"), @@ -26,6 +26,6 @@ def cExec(context:EventContext, data:InputMessageData) -> None: text_plain=text, text_html=f"
{html_escape(text)}
")) RegisterModule(name="System", endpoints=[ - SafeNamespace(names=["exec"], handler=cExec), + SafeNamespace(names=["exec"], handler=cExec, body=True), ]) diff --git a/ModWinDog/System/System.yaml b/ModWinDog/System/System.yaml old mode 100644 new mode 100755 diff --git a/WinDog.py b/WinDog.py index c534d55..f993c54 100755 --- a/WinDog.py +++ b/WinDog.py @@ -49,39 +49,6 @@ def Log(text:str, level:str="?", *, newline:bool|None=None, inline:bool=False) - if LogToFile: open("./Log.txt", 'a').write(text + endline) -def SetupLocales() -> None: - global Locale - for file in listdir('./Locale'): - lang = file.split('.')[0] - try: - with open(f'./Locale/{file}') as file: - Locale[lang] = json.load(file) - except Exception: - Log(f'Cannot load {lang} locale, exiting.') - raise - exit(1) - for key in Locale[DefaultLanguage]: - Locale['Fallback'][key] = Locale[DefaultLanguage][key] - for lang in Locale: - for key in Locale[lang]: - if not key in Locale['Fallback']: - Locale['Fallback'][key] = Locale[lang][key] - def querier(query:str, lang:str=DefaultLanguage): - value = None - query = query.split('.') - try: - value = Locale.Locale[lang] - for key in query: - value = value[key] - except Exception: - value = Locale.Locale['Fallback'] - for key in query: - value = value[key] - return value - Locale['__'] = querier - Locale['Locale'] = Locale - Locale = SimpleNamespace(**Locale) - def SureArray(array:any) -> list|tuple: return (array if type(array) in [list, tuple] else [array]) @@ -112,12 +79,39 @@ def isinstanceSafe(clazz:any, instance:any, /) -> bool: return isinstance(clazz, instance) return False -def get_string(bank:dict, query:str|dict, lang:str=None, /): +def good_yaml_load(text:str): + return yaml_load(text.replace("\t", " "), Loader=yaml_BaseLoader) + +def get_string(bank:dict, query:str|dict, lang:str=None) -> str|list[str]|None: if not (result := ObjGet(bank, f"{query}.{lang or DefaultLanguage}")): if not (result := ObjGet(bank, f"{query}.en")): result = ObjGet(bank, query) return result +def help_text(endpoint, lang:str=None) -> str: + global_string = (lambda query: get_string(GlobalStrings, query, lang)) + text = f'{endpoint.get_string("summary", lang) or ""}\n\n{global_string("usage")}:' + if endpoint.arguments: + for argument in endpoint.arguments: + if endpoint.arguments[argument]: + text += f' <{endpoint.get_string(f"arguments.{argument}", lang) or argument}>' + body_help = endpoint.get_string("body", lang) + quoted_help = (global_string("quoted_message") + (f': {body_help}' if body_help else '')) + if not body_help: + body_help = global_string("text") + if endpoint.body == False and endpoint.quoted == False: + text += f' <{global_string("text")} {global_string("or")} {global_string("quoted_message")}: {body_help}>' + else: + if endpoint.body == True: + text += f' <{body_help}>' + elif endpoint.body == False: + text += f' [{body_help}]' + if endpoint.quoted == True: + text += f' <{quoted_help}>' + elif endpoint.quoted == False: + text += f' [{quoted_help}]' + return text + def strip_url_scheme(url:str) -> str: tokens = urlparse.urlparse(url) return f"{tokens.netloc}{tokens.path}" @@ -172,7 +166,6 @@ def GetUserSettings(user_id:str) -> SafeNamespace|None: except EntitySettings.DoesNotExist: return None -# TODO ignore tagged commands when they are not directed to the bot's username def ParseCommand(text:str) -> SafeNamespace|None: if not text: return None @@ -184,12 +177,15 @@ def ParseCommand(text:str) -> SafeNamespace|None: return None command = SafeNamespace() command.tokens = text.split() - command.name = command.tokens[0][1:].lower().split('@')[0] + command.name, command_target = (command.tokens[0][1:].lower().split('@') + ['']) + # TODO ignore tagged commands when they are not directed to the bot's username + if command_target and not command_target: + return None command.body = text[len(command.tokens[0]):].strip() if command.name not in Endpoints: return command if (endpoint_arguments := Endpoints[command.name].arguments): - command.arguments = {} + command.arguments = SafeNamespace() index = 1 for key in endpoint_arguments: if not endpoint_arguments[key]: @@ -300,14 +296,14 @@ def SendNotice(context:EventContext, data) -> None: def DeleteMessage(context:EventContext, data) -> None: pass -def RegisterPlatform(name:str, main:callable, sender:callable, linker:callable=None, *, event_class=None, manager_class=None) -> None: - Platforms[name.lower()] = SafeNamespace(name=name, main=main, sender=sender, linker=linker, event_class=event_class, manager_class=manager_class) +def RegisterPlatform(name:str, main:callable, sender:callable, linker:callable=None, *, event_class=None, manager_class=None, agent_info=None) -> None: + Platforms[name.lower()] = SafeNamespace(name=name, main=main, sender=sender, linker=linker, event_class=event_class, manager_class=manager_class, agent_info=agent_info) Log(f"{name}, ", inline=True) def RegisterModule(name:str, endpoints:dict, *, group:str|None=None) -> None: - module = SafeNamespace(group=group, endpoints=endpoints, get_string=(lambda query, lang=None, /: None)) + module = SafeNamespace(group=group, endpoints=endpoints, get_string=(lambda query, lang=None: None)) if isfile(file := f"./ModWinDog/{name}/{name}.yaml"): - module.strings = yaml_load(open(file, 'r').read().replace("\t", " "), Loader=yaml_BaseLoader) + module.strings = good_yaml_load(open(file, 'r').read()) module.get_string = (lambda query, lang=None: get_string(module.strings, query, lang)) Modules[name] = module Log(f"{name}, ", inline=True) @@ -318,9 +314,13 @@ def RegisterModule(name:str, endpoints:dict, *, group:str|None=None) -> None: def CallEndpoint(name:str, context:EventContext, data:InputMessageData): endpoint = Endpoints[name] - context.endpoint = endpoint context.module = endpoint.module - context.endpoint.get_string = (lambda query, lang=None, /: endpoint.module.get_string(f"endpoints.{data.command.name}.{query}", lang)) + context.endpoint = endpoint + context.endpoint.get_string = (lambda query=data.command.name, lang=None: + endpoint.module.get_string(f"endpoints.{data.command.name}.{query}", lang)) + context.endpoint.help_text = (lambda lang=None: help_text(endpoint, lang)) + if callable(agent_info := Platforms[context.platform].agent_info): + Platforms[context.platform].agent_info = agent_info() return endpoint.handler(context, data) def WriteNewConfig() -> None: @@ -341,7 +341,6 @@ def WriteNewConfig() -> None: def Main() -> None: #SetupDb() - SetupLocales() Log(f"📨️ Initializing Platforms... ", newline=False) for platform in Platforms.values(): if platform.main(): @@ -353,7 +352,7 @@ def Main() -> None: if __name__ == '__main__': Log("🌞️ WinDog Starting...") - Locale = {"Fallback": {}} + GlobalStrings = good_yaml_load(open("./WinDog.yaml", 'r').read()) Platforms, Modules, ModuleGroups, Endpoints = {}, {}, {}, {} for folder in ("LibWinDog/Platforms", "ModWinDog"): diff --git a/WinDog.yaml b/WinDog.yaml new file mode 100644 index 0000000..ab4ad13 --- /dev/null +++ b/WinDog.yaml @@ -0,0 +1,13 @@ +or: + en: or + it: o +quoted_message: + en: Quoted Message + it: Messaggio Citato +text: + en: Text + it: Testo +usage: + en: Usage + it: Uso +