mirror of
https://gitlab.com/octospacc/WinDog.git
synced 2025-06-05 22:09:20 +02:00
Cross-platform messaging working, preparations for bridges
This commit is contained in:
@ -15,13 +15,13 @@ LogToFile = True
|
|||||||
DumpToConsole = False
|
DumpToConsole = False
|
||||||
DumpToFile = False
|
DumpToFile = False
|
||||||
|
|
||||||
AdminIds = [ "telegram:123456789", "telegram:634314973", "activitypub:admin@mastodon.example.com", ]
|
AdminIds = [ "telegram:123456789", "telegram:634314973", "matrix:@admin:matrix.example.com", "matrix:@octt:matrix.org", "activitypub:admin@mastodon.example.com", ]
|
||||||
|
|
||||||
DefaultLang = "en"
|
BridgesConfig = []
|
||||||
|
|
||||||
|
DefaultLanguage = "en"
|
||||||
Debug = False
|
Debug = False
|
||||||
CmdPrefixes = ".!/"
|
CmdPrefixes = ".!/"
|
||||||
# False: ASCII output; True: ANSI Output (must be escaped)
|
|
||||||
ExecAllowed = {"date": False, "fortune": False, "neofetch": True, "uptime": False}
|
|
||||||
WebUserAgent = "WinDog v.Staging"
|
WebUserAgent = "WinDog v.Staging"
|
||||||
|
|
||||||
#ModuleGroups = (ModuleGroups | {
|
#ModuleGroups = (ModuleGroups | {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# end windog config # """
|
# end windog config # """
|
||||||
|
|
||||||
MastodonUrl, MastodonToken = None, None
|
MastodonUrl = MastodonToken = None
|
||||||
|
|
||||||
import mastodon
|
import mastodon
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
@ -32,7 +32,6 @@ def MastodonMakeInputMessageData(status:dict) -> InputMessageData:
|
|||||||
text_html = status["content"],
|
text_html = status["content"],
|
||||||
)
|
)
|
||||||
data.text_plain = BeautifulSoup(data.text_html, "html.parser").get_text()
|
data.text_plain = BeautifulSoup(data.text_html, "html.parser").get_text()
|
||||||
data.text_auto = GetWeightedText(data.text_html, data.text_plain)
|
|
||||||
command_tokens = data.text_plain.strip().replace("\t", " ").split(" ")
|
command_tokens = data.text_plain.strip().replace("\t", " ").split(" ")
|
||||||
while command_tokens[0].strip().startswith('@') or not command_tokens[0]:
|
while command_tokens[0].strip().startswith('@') or not command_tokens[0]:
|
||||||
command_tokens.pop(0)
|
command_tokens.pop(0)
|
||||||
@ -51,7 +50,7 @@ def MastodonHandler(event, Mastodon):
|
|||||||
if (command := ObjGet(data, "command.name")):
|
if (command := ObjGet(data, "command.name")):
|
||||||
CallEndpoint(command, EventContext(platform="mastodon", event=event, manager=Mastodon), data)
|
CallEndpoint(command, EventContext(platform="mastodon", event=event, manager=Mastodon), data)
|
||||||
|
|
||||||
def MastodonSender(context:EventContext, data:OutputMessageData, destination) -> None:
|
def MastodonSender(context:EventContext, data:OutputMessageData) -> None:
|
||||||
media_results = None
|
media_results = None
|
||||||
if data.media:
|
if data.media:
|
||||||
media_results = []
|
media_results = []
|
||||||
@ -69,5 +68,5 @@ def MastodonSender(context:EventContext, data:OutputMessageData, destination) ->
|
|||||||
visibility=('direct' if context.event['status']['visibility'] == 'direct' else 'unlisted'),
|
visibility=('direct' if context.event['status']['visibility'] == 'direct' else 'unlisted'),
|
||||||
)
|
)
|
||||||
|
|
||||||
RegisterPlatform(name="Mastodon", main=MastodonMain, sender=MastodonSender, managerClass=mastodon.Mastodon)
|
RegisterPlatform(name="Mastodon", main=MastodonMain, sender=MastodonSender, manager_class=mastodon.Mastodon)
|
||||||
|
|
||||||
|
@ -16,11 +16,14 @@
|
|||||||
|
|
||||||
# end windog config # """
|
# end windog config # """
|
||||||
|
|
||||||
MatrixUrl, MatrixUsername, MatrixPassword, MatrixToken = None, None, None, None
|
MatrixUrl = MatrixUsername = MatrixPassword = MatrixToken = None
|
||||||
MatrixClient = None
|
|
||||||
|
|
||||||
from asyncio import run as asyncio_run, create_task as asyncio_create_task
|
import asyncio
|
||||||
import nio
|
import nio
|
||||||
|
import queue
|
||||||
|
|
||||||
|
MatrixClient = None
|
||||||
|
MatrixQueue = []#queue.Queue()
|
||||||
|
|
||||||
def MatrixMain() -> bool:
|
def MatrixMain() -> bool:
|
||||||
if not (MatrixUrl and MatrixUsername and (MatrixPassword or MatrixToken)):
|
if not (MatrixUrl and MatrixUsername and (MatrixPassword or MatrixToken)):
|
||||||
@ -28,6 +31,13 @@ def MatrixMain() -> bool:
|
|||||||
def upgrade_username(new:str):
|
def upgrade_username(new:str):
|
||||||
global MatrixUsername
|
global MatrixUsername
|
||||||
MatrixUsername = new
|
MatrixUsername = new
|
||||||
|
async def queue_handler():
|
||||||
|
asyncio.ensure_future(queue_handler())
|
||||||
|
if not len(MatrixQueue):
|
||||||
|
# avoid 100% CPU usage ☠️
|
||||||
|
time.sleep(0.01)
|
||||||
|
while len(MatrixQueue):
|
||||||
|
MatrixSender(*MatrixQueue.pop(0))
|
||||||
async def client_main() -> None:
|
async def client_main() -> None:
|
||||||
global MatrixClient
|
global MatrixClient
|
||||||
MatrixClient = nio.AsyncClient(MatrixUrl, MatrixUsername)
|
MatrixClient = nio.AsyncClient(MatrixUrl, MatrixUsername)
|
||||||
@ -37,9 +47,10 @@ def MatrixMain() -> bool:
|
|||||||
if (bot_id := ObjGet(login, "user_id")):
|
if (bot_id := ObjGet(login, "user_id")):
|
||||||
upgrade_username(bot_id) # ensure username is fully qualified for the API
|
upgrade_username(bot_id) # ensure username is fully qualified for the API
|
||||||
await MatrixClient.sync(30000) # resync old messages first to "skip read ones"
|
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(MatrixMessageHandler, nio.RoomMessage)
|
||||||
await MatrixClient.sync_forever(timeout=30000)
|
await MatrixClient.sync_forever(timeout=30000)
|
||||||
Thread(target=lambda:asyncio_run(client_main())).start()
|
Thread(target=lambda:asyncio.run(client_main())).start()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def MatrixMakeInputMessageData(room:nio.MatrixRoom, event:nio.RoomMessage) -> InputMessageData:
|
def MatrixMakeInputMessageData(room:nio.MatrixRoom, event:nio.RoomMessage) -> InputMessageData:
|
||||||
@ -47,7 +58,8 @@ def MatrixMakeInputMessageData(room:nio.MatrixRoom, event:nio.RoomMessage) -> In
|
|||||||
message_id = f"matrix:{event.event_id}",
|
message_id = f"matrix:{event.event_id}",
|
||||||
datetime = event.server_timestamp,
|
datetime = event.server_timestamp,
|
||||||
text_plain = event.body,
|
text_plain = event.body,
|
||||||
text_html = event.formatted_body, # note: this could be None
|
text_html = ObjGet(event, "formatted_body"), # this could be unavailable
|
||||||
|
media = ({"url": event.url} if ObjGet(event, "url") else None),
|
||||||
room = SafeNamespace(
|
room = SafeNamespace(
|
||||||
id = f"matrix:{room.room_id}",
|
id = f"matrix:{room.room_id}",
|
||||||
name = room.display_name,
|
name = room.display_name,
|
||||||
@ -69,8 +81,16 @@ async def MatrixMessageHandler(room:nio.MatrixRoom, event:nio.RoomMessage) -> No
|
|||||||
if (command := ObjGet(data, "command.name")):
|
if (command := ObjGet(data, "command.name")):
|
||||||
CallEndpoint(command, EventContext(platform="matrix", event=SafeNamespace(room=room, event=event), manager=MatrixClient), data)
|
CallEndpoint(command, EventContext(platform="matrix", event=SafeNamespace(room=room, event=event), manager=MatrixClient), data)
|
||||||
|
|
||||||
def MatrixSender(context:EventContext, data:OutputMessageData, destination) -> None:
|
def MatrixSender(context:EventContext, data:OutputMessageData):
|
||||||
asyncio_create_task(context.manager.room_send(room_id=context.event.room.room_id, message_type="m.room.message", content={"msgtype": "m.text", "body": data.text_plain}))
|
try:
|
||||||
|
asyncio.get_event_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
MatrixQueue.append((context, data))
|
||||||
|
return None
|
||||||
|
asyncio.create_task(context.manager.room_send(
|
||||||
|
room_id=(data.room_id or ObjGet(context, "event.room.room_id")),
|
||||||
|
message_type="m.room.message",
|
||||||
|
content={"msgtype": "m.text", "body": data.text_plain}))
|
||||||
|
|
||||||
RegisterPlatform(name="Matrix", main=MatrixMain, sender=MatrixSender)
|
RegisterPlatform(name="Matrix", main=MatrixMain, sender=MatrixSender, manager_class=(lambda:MatrixClient))
|
||||||
|
|
||||||
|
@ -12,19 +12,21 @@
|
|||||||
TelegramToken = None
|
TelegramToken = None
|
||||||
|
|
||||||
import telegram, telegram.ext
|
import telegram, telegram.ext
|
||||||
from telegram import ForceReply, Bot #, Update
|
from telegram import Bot #, Update
|
||||||
#from telegram.helpers import escape_markdown
|
#from telegram.helpers import escape_markdown
|
||||||
#from telegram.ext import Application, filters, CommandHandler, MessageHandler, CallbackContext
|
#from telegram.ext import Application, filters, CommandHandler, MessageHandler, CallbackContext
|
||||||
from telegram.utils.helpers import escape_markdown
|
from telegram.utils.helpers import escape_markdown
|
||||||
from telegram.ext import CommandHandler, MessageHandler, Filters, CallbackContext
|
from telegram.ext import CommandHandler, MessageHandler, Filters, CallbackContext
|
||||||
|
|
||||||
|
TelegramClient = None
|
||||||
|
|
||||||
def TelegramMain() -> bool:
|
def TelegramMain() -> bool:
|
||||||
if not TelegramToken:
|
if not TelegramToken:
|
||||||
return False
|
return False
|
||||||
updater = telegram.ext.Updater(TelegramToken)
|
global TelegramClient
|
||||||
dispatcher = updater.dispatcher
|
TelegramClient = telegram.ext.Updater(TelegramToken)
|
||||||
dispatcher.add_handler(MessageHandler(Filters.text | Filters.command, TelegramHandler))
|
TelegramClient.dispatcher.add_handler(MessageHandler(Filters.text | Filters.command, TelegramHandler))
|
||||||
updater.start_polling()
|
TelegramClient.start_polling()
|
||||||
#app = Application.builder().token(TelegramToken).build()
|
#app = Application.builder().token(TelegramToken).build()
|
||||||
#app.add_handler(MessageHandler(filters.TEXT | filters.COMMAND, TelegramHandler))
|
#app.add_handler(MessageHandler(filters.TEXT | filters.COMMAND, TelegramHandler))
|
||||||
#app.run_polling(allowed_updates=Update.ALL_TYPES)
|
#app.run_polling(allowed_updates=Update.ALL_TYPES)
|
||||||
@ -49,7 +51,6 @@ def TelegramMakeInputMessageData(message:telegram.Message) -> InputMessageData:
|
|||||||
name = (message.chat.title or message.chat.first_name),
|
name = (message.chat.title or message.chat.first_name),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
data.text_auto = GetWeightedText(data.text_markdown, data.text_plain)
|
|
||||||
data.command = ParseCommand(data.text_plain)
|
data.command = ParseCommand(data.text_plain)
|
||||||
data.user.settings = (GetUserSettings(data.user.id) or SafeNamespace())
|
data.user.settings = (GetUserSettings(data.user.id) or SafeNamespace())
|
||||||
linked = TelegramLinker(data)
|
linked = TelegramLinker(data)
|
||||||
@ -69,10 +70,10 @@ def TelegramHandler(update:telegram.Update, context:CallbackContext=None) -> Non
|
|||||||
CallEndpoint(command, EventContext(platform="telegram", event=update, manager=context), data)
|
CallEndpoint(command, EventContext(platform="telegram", event=update, manager=context), data)
|
||||||
Thread(target=handler).start()
|
Thread(target=handler).start()
|
||||||
|
|
||||||
def TelegramSender(context:EventContext, data:OutputMessageData, destination):
|
def TelegramSender(context:EventContext, data:OutputMessageData):
|
||||||
result = None
|
result = None
|
||||||
if destination:
|
if data.room_id:
|
||||||
result = context.manager.bot.send_message(destination, text=data.text_plain)
|
result = context.manager.bot.send_message(data.room_id, text=data.text_plain)
|
||||||
else:
|
else:
|
||||||
replyToId = (data.ReplyTo or context.event.message.message_id)
|
replyToId = (data.ReplyTo or context.event.message.message_id)
|
||||||
if data.media:
|
if data.media:
|
||||||
@ -103,5 +104,5 @@ def TelegramLinker(data:InputMessageData) -> SafeNamespace:
|
|||||||
linked.message = f"https://t.me/c/{room_id}/{message_id}"
|
linked.message = f"https://t.me/c/{room_id}/{message_id}"
|
||||||
return linked
|
return linked
|
||||||
|
|
||||||
RegisterPlatform(name="Telegram", main=TelegramMain, sender=TelegramSender, linker=TelegramLinker, eventClass=telegram.Update)
|
RegisterPlatform(name="Telegram", main=TelegramMain, sender=TelegramSender, linker=TelegramLinker, event_class=telegram.Update, manager_class=(lambda:TelegramClient))
|
||||||
|
|
||||||
|
@ -7,10 +7,11 @@ def cBroadcast(context:EventContext, data:InputMessageData) -> None:
|
|||||||
if (data.user.id not in AdminIds) and (data.user.tag not in AdminIds):
|
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": choice(Locale.__('eval'))})
|
||||||
destination = data.command.arguments["destination"]
|
destination = data.command.arguments["destination"]
|
||||||
if not (destination and data.command.body):
|
text = data.command.body
|
||||||
return SendMessage(context, {"Text": "Bad usage."})
|
if not (destination and text):
|
||||||
SendMessage(context, {"TextPlain": data.command.body}, destination)
|
return SendMessage(context, OutputMessageData(text_plain="Bad usage."))
|
||||||
SendMessage(context, {"TextPlain": "Executed."})
|
SendMessage(context, OutputMessageData(text_plain=text, room_id=destination))
|
||||||
|
SendMessage(context, OutputMessageData(text_plain="Executed."))
|
||||||
|
|
||||||
RegisterModule(name="Broadcast", endpoints=[
|
RegisterModule(name="Broadcast", endpoints=[
|
||||||
SafeNamespace(names=["broadcast"], handler=cBroadcast, arguments={
|
SafeNamespace(names=["broadcast"], handler=cBroadcast, arguments={
|
||||||
|
@ -18,15 +18,15 @@ def HttpReq(url:str, method:str|None=None, *, body:bytes=None, headers:dict[str,
|
|||||||
def cEmbedded(context:EventContext, data:InputMessageData) -> None:
|
def cEmbedded(context:EventContext, data:InputMessageData) -> None:
|
||||||
if len(data.command.tokens) >= 2:
|
if len(data.command.tokens) >= 2:
|
||||||
# Find links in command body
|
# Find links in command body
|
||||||
Text = (data.text_markdown + ' ' + data.text_plain)
|
text = (data.text_markdown + ' ' + data.text_plain)
|
||||||
elif data.quoted and data.quoted.text_auto:
|
elif (quoted := data.quoted) and (quoted.text_auto or quoted.text_markdown or quoted.text_html):
|
||||||
# Find links in quoted message
|
# Find links in quoted message
|
||||||
Text = (data.quoted.text_markdown + ' ' + data.quoted.text_plain)
|
text = ((quoted.text_markdown or '') + ' ' + (quoted.text_plain or '') + ' ' + (quoted.text_html or ''))
|
||||||
else:
|
else:
|
||||||
# TODO Error message
|
# TODO Error message
|
||||||
return
|
return
|
||||||
pass
|
pass
|
||||||
urls = URLExtract().find_urls(Text)
|
urls = URLExtract().find_urls(text)
|
||||||
if len(urls) > 0:
|
if len(urls) > 0:
|
||||||
proto = 'https://'
|
proto = 'https://'
|
||||||
url = urls[0]
|
url = urls[0]
|
||||||
@ -47,7 +47,7 @@ def cEmbedded(context:EventContext, data:InputMessageData) -> None:
|
|||||||
elif urlDomain == "vm.tiktok.com":
|
elif urlDomain == "vm.tiktok.com":
|
||||||
urlDomain = "vm.vxtiktok.com"
|
urlDomain = "vm.vxtiktok.com"
|
||||||
url = (urlDomain + '/' + '/'.join(url.split('/')[1:]))
|
url = (urlDomain + '/' + '/'.join(url.split('/')[1:]))
|
||||||
SendMessage(context, {"TextPlain": f"{{{proto}{url}}}"})
|
SendMessage(context, {"text_plain": f"{{{proto}{url}}}"})
|
||||||
# else TODO error message?
|
# else TODO error message?
|
||||||
|
|
||||||
def cWeb(context:EventContext, data:InputMessageData) -> None:
|
def cWeb(context:EventContext, data:InputMessageData) -> None:
|
||||||
@ -79,13 +79,13 @@ def cNews(context:EventContext, data:InputMessageData) -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def cTranslate(context:EventContext, data:InputMessageData) -> None:
|
def cTranslate(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
instances = ["lingva.ml", "lingva.lunar.icu"]
|
||||||
language_to = data.command.arguments["language_to"]
|
language_to = data.command.arguments["language_to"]
|
||||||
text_input = (data.command.body or (data.quoted and data.quoted.text_plain))
|
text_input = (data.command.body or (data.quoted and data.quoted.text_plain))
|
||||||
if not (text_input and language_to):
|
if not (text_input and language_to):
|
||||||
return SendMessage(context, {"TextPlain": f"Usage: /translate <to language> <text>"})
|
return SendMessage(context, {"TextPlain": f"Usage: /translate <to language> <text>"})
|
||||||
try:
|
try:
|
||||||
# TODO: Use many different public Lingva instances in rotation to avoid overloading a specific one
|
result = json.loads(HttpReq(f'https://{randchoice(instances)}/api/v1/auto/{language_to}/{urlparse.quote(text_input)}').read())
|
||||||
result = json.loads(HttpReq(f'https://lingva.ml/api/v1/auto/{language_to}/{urlparse.quote(text_input)}').read())
|
|
||||||
SendMessage(context, {"TextPlain": f"[{result['info']['detectedSource']} (auto) -> {language_to}]\n\n{result['translation']}"})
|
SendMessage(context, {"TextPlain": f"[{result['info']['detectedSource']} (auto) -> {language_to}]\n\n{result['translation']}"})
|
||||||
except Exception:
|
except Exception:
|
||||||
raise
|
raise
|
||||||
|
@ -3,6 +3,13 @@
|
|||||||
# Licensed under AGPLv3 by OctoSpacc #
|
# Licensed under AGPLv3 by OctoSpacc #
|
||||||
# ==================================== #
|
# ==================================== #
|
||||||
|
|
||||||
|
""" # windog config start # """
|
||||||
|
|
||||||
|
# False: ASCII output; True: ANSI Output (must be escaped)
|
||||||
|
ExecAllowed = {"date": False, "fortune": False, "neofetch": True, "uptime": False}
|
||||||
|
|
||||||
|
""" # end windog config # """
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from re import compile as re_compile
|
from re import compile as re_compile
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ WinDog/WinDogBot is a chatbot I've been (lazily) developing for years, with some
|
|||||||
The officially-hosted instances of this bot are, respectively:
|
The officially-hosted instances of this bot are, respectively:
|
||||||
|
|
||||||
* [@WinDogBot](https://t.me/WinDogBot) on Telegram
|
* [@WinDogBot](https://t.me/WinDogBot) on Telegram
|
||||||
|
* [@windog:matrix.org](https://matrix.to/#/@windog:matrix.org) on Matrix
|
||||||
* [@WinDog@botsin.space](https://botsin.space/@WinDog) on Mastodon (can also be used from any other Fediverse platform)
|
* [@WinDog@botsin.space](https://botsin.space/@WinDog) on Mastodon (can also be used from any other Fediverse platform)
|
||||||
|
|
||||||
In case you want to run your own instance:
|
In case you want to run your own instance:
|
||||||
|
55
WinDog.py
55
WinDog.py
@ -13,8 +13,8 @@ from os import listdir
|
|||||||
from os.path import isfile, isdir
|
from os.path import isfile, isdir
|
||||||
from random import choice, choice as randchoice, randint
|
from random import choice, choice as randchoice, randint
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from traceback import format_exc
|
from traceback import format_exc, format_exc as traceback_format_exc
|
||||||
from urllib import parse as urlparse
|
from urllib import parse as urlparse, urllib_parse
|
||||||
from yaml import load as yaml_load, BaseLoader as yaml_BaseLoader
|
from yaml import load as yaml_load, BaseLoader as yaml_BaseLoader
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
@ -25,12 +25,16 @@ from LibWinDog.Database import *
|
|||||||
# <https://daringfireball.net/projects/markdown/syntax#backslash>
|
# <https://daringfireball.net/projects/markdown/syntax#backslash>
|
||||||
MdEscapes = '\\`*_{}[]()<>#+-.!|='
|
MdEscapes = '\\`*_{}[]()<>#+-.!|='
|
||||||
|
|
||||||
def NamespaceUnion(namespaces:list|tuple, clazz=SimpleNamespace):
|
def ObjectUnion(*objects:object, clazz:object=None):
|
||||||
dikt = {}
|
dikt = {}
|
||||||
for namespace in namespaces:
|
auto_clazz = None
|
||||||
for key, value in tuple(namespace.__dict__.items()):
|
for obj in objects:
|
||||||
|
if type(obj) == dict:
|
||||||
|
obj = (clazz or SafeNamespace)(**obj)
|
||||||
|
for key, value in tuple(obj.__dict__.items()):
|
||||||
dikt[key] = value
|
dikt[key] = value
|
||||||
return clazz(**dikt)
|
auto_clazz = obj.__class__
|
||||||
|
return (clazz or auto_clazz)(**dikt)
|
||||||
|
|
||||||
def Log(text:str, level:str="?", *, newline:bool|None=None, inline:bool=False) -> None:
|
def Log(text:str, level:str="?", *, newline:bool|None=None, inline:bool=False) -> None:
|
||||||
endline = '\n'
|
endline = '\n'
|
||||||
@ -53,13 +57,13 @@ def SetupLocales() -> None:
|
|||||||
Log(f'Cannot load {lang} locale, exiting.')
|
Log(f'Cannot load {lang} locale, exiting.')
|
||||||
raise
|
raise
|
||||||
exit(1)
|
exit(1)
|
||||||
for key in Locale[DefaultLang]:
|
for key in Locale[DefaultLanguage]:
|
||||||
Locale['Fallback'][key] = Locale[DefaultLang][key]
|
Locale['Fallback'][key] = Locale[DefaultLanguage][key]
|
||||||
for lang in Locale:
|
for lang in Locale:
|
||||||
for key in Locale[lang]:
|
for key in Locale[lang]:
|
||||||
if not key in Locale['Fallback']:
|
if not key in Locale['Fallback']:
|
||||||
Locale['Fallback'][key] = Locale[lang][key]
|
Locale['Fallback'][key] = Locale[lang][key]
|
||||||
def querier(query:str, lang:str=DefaultLang):
|
def querier(query:str, lang:str=DefaultLanguage):
|
||||||
value = None
|
value = None
|
||||||
query = query.split('.')
|
query = query.split('.')
|
||||||
try:
|
try:
|
||||||
@ -106,7 +110,7 @@ def isinstanceSafe(clazz:any, instance:any, /) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def get_string(bank:dict, query:str|dict, lang:str=None, /):
|
def get_string(bank:dict, query:str|dict, lang:str=None, /):
|
||||||
if not (result := ObjGet(bank, f"{query}.{lang or DefaultLang}")):
|
if not (result := ObjGet(bank, f"{query}.{lang or DefaultLanguage}")):
|
||||||
if not (result := ObjGet(bank, f"{query}.en")):
|
if not (result := ObjGet(bank, f"{query}.en")):
|
||||||
result = ObjGet(bank, query)
|
result = ObjGet(bank, query)
|
||||||
return result
|
return result
|
||||||
@ -199,6 +203,13 @@ def ParseCommand(text:str) -> SafeNamespace|None:
|
|||||||
def OnMessageParsed(data:InputMessageData) -> None:
|
def OnMessageParsed(data:InputMessageData) -> None:
|
||||||
DumpMessage(data)
|
DumpMessage(data)
|
||||||
UpdateUserDb(data.user)
|
UpdateUserDb(data.user)
|
||||||
|
for bridge in BridgesConfig:
|
||||||
|
if data.room.id in bridge:
|
||||||
|
rooms = list(bridge)
|
||||||
|
rooms.remove(data.room.id)
|
||||||
|
for room in rooms:
|
||||||
|
tokens = room.split(':')
|
||||||
|
SendMessage(SafeNamespace(platform=tokens[0]), ObjectUnion(data, {"room_id": ':'.join(tokens)}))
|
||||||
|
|
||||||
def UpdateUserDb(user:SafeNamespace) -> None:
|
def UpdateUserDb(user:SafeNamespace) -> None:
|
||||||
try:
|
try:
|
||||||
@ -214,14 +225,14 @@ def UpdateUserDb(user:SafeNamespace) -> None:
|
|||||||
def DumpMessage(data:InputMessageData) -> None:
|
def DumpMessage(data:InputMessageData) -> None:
|
||||||
if not (Debug and (DumpToFile or DumpToConsole)):
|
if not (Debug and (DumpToFile or DumpToConsole)):
|
||||||
return
|
return
|
||||||
text = (data.text_plain.replace('\n', '\\n') if data.text_auto else '')
|
text = (data.text_plain.replace('\n', '\\n') if data.text_plain else '')
|
||||||
text = f"[{int(time.time())}] [{time.ctime()}] [{data.room and data.room.id}] [{data.message_id}] [{data.user.id}] {text}"
|
text = f"[{int(time.time())}] [{time.ctime()}] [{data.room and data.room.id}] [{data.message_id}] [{data.user.id}] {text}"
|
||||||
if DumpToConsole:
|
if DumpToConsole:
|
||||||
print(text, data)
|
print(text, data)
|
||||||
if DumpToFile:
|
if DumpToFile:
|
||||||
open((DumpToFile if (DumpToFile and type(DumpToFile) == str) else "./Dump.txt"), 'a').write(text + '\n')
|
open((DumpToFile if (DumpToFile and type(DumpToFile) == str) else "./Dump.txt"), 'a').write(text + '\n')
|
||||||
|
|
||||||
def SendMessage(context:EventContext, data:OutputMessageData, destination=None) -> None:
|
def SendMessage(context:EventContext, data:OutputMessageData) -> None:
|
||||||
data = (OutputMessageData(**data) if type(data) == dict else data)
|
data = (OutputMessageData(**data) if type(data) == dict else data)
|
||||||
|
|
||||||
# TODO remove this after all modules are changed
|
# TODO remove this after all modules are changed
|
||||||
@ -244,10 +255,18 @@ def SendMessage(context:EventContext, data:OutputMessageData, destination=None)
|
|||||||
#data.text_html = ???
|
#data.text_html = ???
|
||||||
if data.media:
|
if data.media:
|
||||||
data.media = SureArray(data.media)
|
data.media = SureArray(data.media)
|
||||||
#for platform in Platforms.values():
|
if data.room_id:
|
||||||
# if isinstanceSafe(context.event, platform.eventClass) or isinstanceSafe(context.manager, platform.managerClass):
|
tokens = data.room_id.split(':')
|
||||||
# return platform.sender(context, data, destination)
|
if tokens[0] != context.platform:
|
||||||
return Platforms[context.platform].sender(context, data, destination)
|
context.platform = tokens[0]
|
||||||
|
context.manager = context.event = None
|
||||||
|
data.room_id = ':'.join(tokens[1:])
|
||||||
|
if context.platform not in Platforms:
|
||||||
|
return None
|
||||||
|
platform = Platforms[context.platform]
|
||||||
|
if (not context.manager) and (manager := platform.manager_class):
|
||||||
|
context.manager = (manager() if callable(manager) else manager)
|
||||||
|
return platform.sender(context, data)
|
||||||
|
|
||||||
def SendNotice(context:EventContext, data) -> None:
|
def SendNotice(context:EventContext, data) -> None:
|
||||||
pass
|
pass
|
||||||
@ -255,8 +274,8 @@ def SendNotice(context:EventContext, data) -> None:
|
|||||||
def DeleteMessage(context:EventContext, data) -> None:
|
def DeleteMessage(context:EventContext, data) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def RegisterPlatform(name:str, main:callable, sender:callable, linker:callable=None, *, eventClass=None, managerClass=None) -> None:
|
def RegisterPlatform(name:str, main:callable, sender:callable, linker:callable=None, *, event_class=None, manager_class=None) -> None:
|
||||||
Platforms[name.lower()] = SafeNamespace(main=main, sender=sender, linker=linker, eventClass=eventClass, managerClass=managerClass)
|
Platforms[name.lower()] = SafeNamespace(main=main, sender=sender, linker=linker, event_class=event_class, manager_class=manager_class)
|
||||||
Log(f"{name}, ", inline=True)
|
Log(f"{name}, ", inline=True)
|
||||||
|
|
||||||
def RegisterModule(name:str, endpoints:dict, *, group:str|None=None) -> None:
|
def RegisterModule(name:str, endpoints:dict, *, group:str|None=None) -> None:
|
||||||
|
Reference in New Issue
Block a user