mirror of
https://gitlab.com/octospacc/WinDog.git
synced 2025-06-05 22:09:20 +02:00
Misc updates, improve global API, start work on db and module strings
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,6 +3,6 @@
|
|||||||
/Dump.txt
|
/Dump.txt
|
||||||
/Log.txt
|
/Log.txt
|
||||||
/Selenium-WinDog/
|
/Selenium-WinDog/
|
||||||
/downloaded_files
|
/downloaded_files/
|
||||||
/session.txt
|
/session.txt
|
||||||
*.pyc
|
*.pyc
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
# ================================== #
|
# ================================== #
|
||||||
""" # windog config start # """
|
""" # windog config start # """
|
||||||
|
|
||||||
# If you have modified the bot's code, you should set this
|
# If you have modified the bot's code, you should set this.
|
||||||
ModifiedSourceUrl = ""
|
ModifiedSourceUrl = ""
|
||||||
|
|
||||||
|
# Logging of system information and runtime errors. Recommended to be on at least for console.
|
||||||
LogToConsole = True
|
LogToConsole = True
|
||||||
LogToFile = True
|
LogToFile = True
|
||||||
|
|
||||||
|
# Dumping of the bot's remote events. Should stay off unless needed for debugging.
|
||||||
DumpToConsole = False
|
DumpToConsole = False
|
||||||
DumpToFile = False
|
DumpToFile = False
|
||||||
|
|
||||||
|
@ -6,3 +6,17 @@ class BaseModel(Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
database = Db
|
database = Db
|
||||||
|
|
||||||
|
class Entity(BaseModel):
|
||||||
|
id = CharField(null=True)
|
||||||
|
id_hash = CharField()
|
||||||
|
#settings = ForeignKeyField(EntitySettings, backref="entity")
|
||||||
|
#language = CharField(null=True)
|
||||||
|
|
||||||
|
class User(Entity):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Room(Entity):
|
||||||
|
pass
|
||||||
|
|
||||||
|
Db.create_tables([User, Room], safe=True)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ MastodonUrl, MastodonToken = None, None
|
|||||||
|
|
||||||
import mastodon
|
import mastodon
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
from magic import Magic
|
||||||
|
|
||||||
def MastodonMain() -> bool:
|
def MastodonMain() -> bool:
|
||||||
if not (MastodonUrl and MastodonToken):
|
if not (MastodonUrl and MastodonToken):
|
||||||
@ -36,19 +37,19 @@ def MastodonHandler(event):
|
|||||||
if command:
|
if command:
|
||||||
command.messageId = event['status']['id']
|
command.messageId = event['status']['id']
|
||||||
if command.Name in Endpoints:
|
if command.Name in Endpoints:
|
||||||
Endpoints[command.Name]["handler"]({"Event": event, "Manager": Mastodon}, command)
|
CallEndpoint(command.Name, EventContext(platform="mastodon", event=event, manager=Mastodon), command)
|
||||||
|
|
||||||
def MastodonSender(event, manager, data:OutputMessageData, destination, textPlain, textMarkdown) -> None:
|
def MastodonSender(context:EventContext, data:OutputMessageData, destination, textPlain, textMarkdown) -> None:
|
||||||
if InDict(data, 'Media'):
|
if InDict(data, 'Media'):
|
||||||
Media = manager.media_post(data['Media'], Magic(mime=True).from_buffer(data['Media']))
|
Media = context.manager.media_post(data['Media'], Magic(mime=True).from_buffer(data['Media']))
|
||||||
while Media['url'] == 'null':
|
while Media['url'] == 'null':
|
||||||
Media = manager.media(Media)
|
Media = context.manager.media(Media)
|
||||||
if textPlain or Media:
|
if textPlain or Media:
|
||||||
manager.status_post(
|
context.manager.status_post(
|
||||||
status=(textPlain + '\n\n@' + event['account']['acct']),
|
status=(textPlain + '\n\n@' + context.event['account']['acct']),
|
||||||
media_ids=(Media if InDict(data, 'Media') else None),
|
media_ids=(Media if InDict(data, 'Media') else None),
|
||||||
in_reply_to_id=event['status']['id'],
|
in_reply_to_id=context.event['status']['id'],
|
||||||
visibility=('direct' if 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, managerClass=mastodon.Mastodon)
|
||||||
|
@ -41,13 +41,15 @@ def TelegramMakeInputMessageData(message:telegram.Message) -> InputMessageData:
|
|||||||
data.room = SafeNamespace(
|
data.room = SafeNamespace(
|
||||||
id = f"telegram:{message.chat.id}",
|
id = f"telegram:{message.chat.id}",
|
||||||
tag = message.chat.username,
|
tag = message.chat.username,
|
||||||
name = message.chat.title,
|
name = (message.chat.title or message.chat.first_name),
|
||||||
)
|
)
|
||||||
data.user = SafeNamespace(
|
data.user = SafeNamespace(
|
||||||
id = f"telegram:{message.from_user.id}",
|
id = f"telegram:{message.from_user.id}",
|
||||||
tag = message.from_user.username,
|
tag = message.from_user.username,
|
||||||
name = message.from_user.first_name,
|
name = message.from_user.first_name,
|
||||||
)
|
)
|
||||||
|
#if (db_user := GetUserData(data.user.id)):
|
||||||
|
# data.user.language = db_user.language
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def TelegramHandlerWrapper(update:telegram.Update, context:CallbackContext=None) -> None:
|
def TelegramHandlerWrapper(update:telegram.Update, context:CallbackContext=None) -> None:
|
||||||
@ -63,6 +65,7 @@ def TelegramHandlerCore(update:telegram.Update, context:CallbackContext=None) ->
|
|||||||
cmd = ParseCmd(update.message.text)
|
cmd = ParseCmd(update.message.text)
|
||||||
if cmd:
|
if cmd:
|
||||||
cmd.command = data.command
|
cmd.command = data.command
|
||||||
|
cmd.quoted = data.quoted
|
||||||
cmd.messageId = update.message.message_id
|
cmd.messageId = update.message.message_id
|
||||||
cmd.TextPlain = cmd.Body
|
cmd.TextPlain = cmd.Body
|
||||||
cmd.TextMarkdown = update.message.text_markdown_v2
|
cmd.TextMarkdown = update.message.text_markdown_v2
|
||||||
@ -86,27 +89,28 @@ def TelegramHandlerCore(update:telegram.Update, context:CallbackContext=None) ->
|
|||||||
"Id": f'telegram:{update.message.reply_to_message.from_user.id}',
|
"Id": f'telegram:{update.message.reply_to_message.from_user.id}',
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
Endpoints[cmd.Name]["handler"]({"Event": update, "Manager": context}, cmd)
|
CallEndpoint(cmd.Name, EventContext(platform="telegram", event=update, manager=context), cmd)
|
||||||
#Endpoints[cmd.Name]["handler"](SafeNamespace(platform="telegram", event=update, manager=context), cmd)
|
|
||||||
|
|
||||||
def TelegramSender(event, manager, data:OutputMessageData, destination, textPlain, textMarkdown) -> None:
|
def TelegramSender(context:EventContext, data:OutputMessageData, destination, textPlain, textMarkdown):
|
||||||
|
result = None
|
||||||
if destination:
|
if destination:
|
||||||
manager.bot.send_message(destination, text=textPlain)
|
result = context.manager.bot.send_message(destination, text=textPlain)
|
||||||
else:
|
else:
|
||||||
replyToId = (data["ReplyTo"] if ("ReplyTo" in data and data["ReplyTo"]) else event.message.message_id)
|
replyToId = (data["ReplyTo"] if ("ReplyTo" in data and data["ReplyTo"]) else context.event.message.message_id)
|
||||||
if InDict(data, "Media") and not InDict(data, "media"):
|
if InDict(data, "Media") and not InDict(data, "media"):
|
||||||
data["media"] = {"bytes": data["Media"]}
|
data["media"] = {"bytes": data["Media"]}
|
||||||
if InDict(data, "media"):
|
if InDict(data, "media"):
|
||||||
for medium in SureArray(data["media"]):
|
for medium in SureArray(data["media"]):
|
||||||
event.message.reply_photo(
|
result = context.event.message.reply_photo(
|
||||||
(DictGet(medium, "bytes") or DictGet(medium, "url")),
|
(DictGet(medium, "bytes") or DictGet(medium, "url")),
|
||||||
caption=(textMarkdown if textMarkdown else textPlain if textPlain else None),
|
caption=(textMarkdown if textMarkdown else textPlain if textPlain else None),
|
||||||
parse_mode=("MarkdownV2" if textMarkdown else None),
|
parse_mode=("MarkdownV2" if textMarkdown else None),
|
||||||
reply_to_message_id=replyToId)
|
reply_to_message_id=replyToId)
|
||||||
elif textMarkdown:
|
elif textMarkdown:
|
||||||
event.message.reply_markdown_v2(textMarkdown, reply_to_message_id=replyToId)
|
result = context.event.message.reply_markdown_v2(textMarkdown, reply_to_message_id=replyToId)
|
||||||
elif textPlain:
|
elif textPlain:
|
||||||
event.message.reply_text(textPlain, reply_to_message_id=replyToId)
|
result = context.event.message.reply_text(textPlain, reply_to_message_id=replyToId)
|
||||||
|
return TelegramMakeInputMessageData(result)
|
||||||
|
|
||||||
def TelegramLinker(data:InputMessageData) -> SafeNamespace:
|
def TelegramLinker(data:InputMessageData) -> SafeNamespace:
|
||||||
linked = SafeNamespace()
|
linked = SafeNamespace()
|
||||||
|
65
ModWinDog/Base/Base.py
Executable file
65
ModWinDog/Base/Base.py
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
# ==================================== #
|
||||||
|
# WinDog multi-purpose chatbot #
|
||||||
|
# Licensed under AGPLv3 by OctoSpacc #
|
||||||
|
# ==================================== #
|
||||||
|
|
||||||
|
import re, subprocess
|
||||||
|
|
||||||
|
def cStart(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
SendMessage(context, {"Text": choice(Locale.__('start')).format(data.User.Name)})
|
||||||
|
|
||||||
|
def cSource(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
SendMessage(context, {"TextPlain": ("""\
|
||||||
|
* Original Code: {https://gitlab.com/octospacc/WinDog}
|
||||||
|
* Mirror: {https://github.com/octospacc/WinDog}
|
||||||
|
""" + (f"* Modified Code: {{{ModifiedSourceUrl}}}" if ModifiedSourceUrl else ""))})
|
||||||
|
|
||||||
|
def cGdpr(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Module: Config
|
||||||
|
# ...
|
||||||
|
#def cConfig(update:telegram.Update, context:CallbackContext) -> None:
|
||||||
|
# Cmd = TelegramHandleCmd(update)
|
||||||
|
# if not Cmd: return
|
||||||
|
# # ... area: eu, us, ...
|
||||||
|
# # ... language: en, it, ...
|
||||||
|
# # ... userdata: import, export, delete
|
||||||
|
|
||||||
|
def cPing(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
SendMessage(context, {"Text": "*Pong!*"})
|
||||||
|
|
||||||
|
#def cTime(update:Update, context:CallbackContext) -> None:
|
||||||
|
# update.message.reply_markdown_v2(
|
||||||
|
# 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 cExec(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
if len(data.Tokens) >= 2 and data.Tokens[1].lower() in ExecAllowed:
|
||||||
|
cmd = data.Tokens[1].lower()
|
||||||
|
Out = subprocess.run(('sh', '-c', f'export PATH=$PATH:/usr/games; {cmd}'),
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode()
|
||||||
|
# <https://stackoverflow.com/a/14693789>
|
||||||
|
Caption = (re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])').sub('', Out))
|
||||||
|
SendMessage(context, {
|
||||||
|
"TextPlain": Caption,
|
||||||
|
"TextMarkdown": MarkdownCode(Caption, True),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
SendMessage(context, {"Text": choice(Locale.__('eval'))})
|
||||||
|
|
||||||
|
RegisterModule(name="Misc", endpoints=[
|
||||||
|
SafeNamespace(names=["start"], summary="Salutes the user, hinting that the bot is working and providing basic quick help.", handler=cStart),
|
||||||
|
SafeNamespace(names=["source"], summary="Provides a copy of the bot source codes and/or instructions on how to get it.", handler=cSource),
|
||||||
|
#SafeNamespace(names=["gdpr"], summary="Operations for european citizens regarding your personal data.", handler=cGdpr),
|
||||||
|
SafeNamespace(names=["ping"], summary="Responds pong, useful for testing messaging latency.", handler=cPing),
|
||||||
|
SafeNamespace(names=["eval"], summary="Execute a Python command (or safe literal operation) in the current context. Currently not implemented.", handler=cEval),
|
||||||
|
SafeNamespace(names=["exec"], summary="Execute a system command from the allowed ones and return stdout+stderr.", handler=cExec),
|
||||||
|
#SafeNamespace(names=["format"], summary="Reformat text using an handful of rules. Not yet implemented.", handler=cFormat),
|
||||||
|
#SafeNamespace(names=["frame"], summary="Frame someone's message into a platform-styled image. Not yet implemented.", handler=cFrame),
|
||||||
|
#SafeNamespace(names=["repeat"], summary="I had this planned but I don't remember what this should have done. Not yet implemented.", handler=cRepeat),
|
||||||
|
])
|
||||||
|
|
@ -12,9 +12,9 @@ def cBroadcast(context:EventContext, data:InputMessageData) -> None:
|
|||||||
SendMessage(context, {"TextPlain": data.command.body}, destination)
|
SendMessage(context, {"TextPlain": data.command.body}, destination)
|
||||||
SendMessage(context, {"TextPlain": "Executed."})
|
SendMessage(context, {"TextPlain": "Executed."})
|
||||||
|
|
||||||
RegisterModule(name="Broadcast", endpoints={
|
RegisterModule(name="Broadcast", endpoints=[
|
||||||
"Broadcast": CreateEndpoint(["broadcast"], summary="Sends an admin message over to any chat destination.", handler=cBroadcast, arguments={
|
SafeNamespace(names=["broadcast"], summary="Sends an admin message over to any chat destination.", handler=cBroadcast, arguments={
|
||||||
"destination": True,
|
"destination": True,
|
||||||
}),
|
}),
|
||||||
})
|
])
|
||||||
|
|
16
ModWinDog/Dumper/Dumper.py
Executable file
16
ModWinDog/Dumper/Dumper.py
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
# ================================== #
|
||||||
|
# WinDog multi-purpose chatbot #
|
||||||
|
# Licensed under AGPLv3 by OctoSpacc #
|
||||||
|
# ================================== #
|
||||||
|
|
||||||
|
from json import dumps as json_dumps
|
||||||
|
|
||||||
|
def cDump(context:EventContext, data:InputMessageData):
|
||||||
|
if not (message := ObjGet(data, "quoted")):
|
||||||
|
pass
|
||||||
|
SendMessage(context, {"TextPlain": json_dumps(message, default=(lambda obj: obj.__dict__), indent=" ")})
|
||||||
|
|
||||||
|
RegisterModule(name="Dumper", group="Geek", endpoints=[
|
||||||
|
SafeNamespace(names=["dump"], handler=cDump),
|
||||||
|
])
|
||||||
|
|
6
ModWinDog/Dumper/Dumper.yaml
Normal file
6
ModWinDog/Dumper/Dumper.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
summary:
|
||||||
|
en: Functions for dumping of various data.
|
||||||
|
dump:
|
||||||
|
summary:
|
||||||
|
en: Dumps the data for a quoted message and returns the JSON representation.
|
||||||
|
|
27
ModWinDog/Echo/Echo.py
Normal file
27
ModWinDog/Echo/Echo.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# ==================================== #
|
||||||
|
# WinDog multi-purpose chatbot #
|
||||||
|
# Licensed under AGPLv3 by OctoSpacc #
|
||||||
|
# ==================================== #
|
||||||
|
|
||||||
|
def cEcho(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
text = ObjGet(data, "command.body")
|
||||||
|
if text:
|
||||||
|
prefix = "🗣️ "
|
||||||
|
#prefix = f"[🗣️]({context.linker(data).message}) "
|
||||||
|
if len(data.Tokens) == 2:
|
||||||
|
nonascii = True
|
||||||
|
for char in data.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)
|
||||||
|
prefix = ''
|
||||||
|
SendMessage(context, {"Text": (prefix + text)})
|
||||||
|
else:
|
||||||
|
SendMessage(context, {"Text": choice(Locale.__('echo.empty'))}) #context.endpoint.get_string('empty')
|
||||||
|
|
||||||
|
RegisterModule(name="Echo", endpoints=[
|
||||||
|
SafeNamespace(names=["echo"], handler=cEcho),
|
||||||
|
])
|
||||||
|
|
8
ModWinDog/Echo/Echo.yaml
Normal file
8
ModWinDog/Echo/Echo.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
endpoints:
|
||||||
|
echo:
|
||||||
|
summary:
|
||||||
|
en: Responds back with the original text of the received message.
|
||||||
|
empty:
|
||||||
|
en: <b>Echo what? Give me something to repeat.</b>
|
||||||
|
it: <b>Echo cosa? Dimmi qualcosa da ripetere.</b>
|
||||||
|
|
@ -10,14 +10,14 @@ g4fClient = G4FClient()
|
|||||||
def cGpt(context:EventContext, data:InputMessageData) -> None:
|
def cGpt(context:EventContext, data:InputMessageData) -> None:
|
||||||
if not data.command.body:
|
if not data.command.body:
|
||||||
return SendMessage(context, {"Text": "You must type some text."})
|
return SendMessage(context, {"Text": "You must type some text."})
|
||||||
output = ""
|
output = None
|
||||||
while not output or output.startswith("sorry, 您的ip已由于触发防滥用检测而被封禁,本服务网址是"): # quick fix
|
while not output or output.startswith("sorry, 您的ip已由于触发防滥用检测而被封禁,本服务网址是"): # quick fix for a strange ratelimit message
|
||||||
output = ""
|
output = ""
|
||||||
for completion in g4fClient.chat.completions.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": data.command.body}], stream=True):
|
for completion in g4fClient.chat.completions.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": data.command.body}], stream=True):
|
||||||
output += (completion.choices[0].delta.content or "")
|
output += (completion.choices[0].delta.content or "")
|
||||||
return SendMessage(context, {"TextPlain": f"[🤖️ GPT]\n\n{output}"})
|
return SendMessage(context, {"TextPlain": f"[🤖️ GPT]\n\n{output}"})
|
||||||
|
|
||||||
RegisterModule(name="ChatGPT", endpoints={
|
RegisterModule(name="ChatGPT", endpoints=[
|
||||||
"GPT": CreateEndpoint(["gpt", "chatgpt"], summary="Sends a message to GPT to get back a response. Note: conversations are not yet supported, and this is more standard GPT than ChatGPT, and in general there are many bugs!", handler=cGpt),
|
SafeNamespace(names=["gpt", "chatgpt"], summary="Sends a message to GPT to get back a response. Note: conversations are not yet supported, and this is more standard GPT than ChatGPT, and in general there are many bugs!", handler=cGpt),
|
||||||
})
|
])
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
# ================================== #
|
|
||||||
# WinDog multi-purpose chatbot #
|
|
||||||
# Licensed under AGPLv3 by OctoSpacc #
|
|
||||||
# ================================== #
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
def cHash(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
algorithm = data.command.arguments["algorithm"]
|
|
||||||
if data.command.body and algorithm in hashlib.algorithms_available:
|
|
||||||
hashed = hashlib.new(algorithm, data.command.body.encode()).hexdigest()
|
|
||||||
SendMessage(context, {
|
|
||||||
"TextPlain": hashed,
|
|
||||||
"TextMarkdown": MarkdownCode(hashed, True),
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
SendMessage(context, {"Text": choice(Locale.__('hash.usage')).format(data.command.tokens[0], hashlib.algorithms_available)})
|
|
||||||
|
|
||||||
RegisterModule(name="Hashing", group="Geek", summary="Functions for hashing of textual content.", endpoints={
|
|
||||||
"Hash": CreateEndpoint(names=["hash"], summary="Responds with the hash-sum of a message received.", handler=cHash, arguments={
|
|
||||||
"algorithm": True,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
24
ModWinDog/Hashing/Hashing.py
Executable file
24
ModWinDog/Hashing/Hashing.py
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
# ================================== #
|
||||||
|
# WinDog multi-purpose chatbot #
|
||||||
|
# Licensed under AGPLv3 by OctoSpacc #
|
||||||
|
# ================================== #
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
def cHash(context:EventContext, data:InputMessageData):
|
||||||
|
text_input = ObjGet(data, "command.body")
|
||||||
|
algorithm = ObjGet(data, "command.arguments.algorithm")
|
||||||
|
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)})
|
||||||
|
hashed = hashlib.new(algorithm, text_input.encode()).hexdigest()
|
||||||
|
return SendMessage(context, {
|
||||||
|
"TextPlain": hashed,
|
||||||
|
"TextMarkdown": MarkdownCode(hashed, True),
|
||||||
|
})
|
||||||
|
|
||||||
|
RegisterModule(name="Hashing", group="Geek", summary="Functions for hashing of textual content.", endpoints=[
|
||||||
|
SafeNamespace(names=["hash"], summary="Responds with the hash-sum of a message received.", handler=cHash, arguments={
|
||||||
|
"algorithm": True,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
14
ModWinDog/Hashing/Hashing.yaml
Normal file
14
ModWinDog/Hashing/Hashing.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
summary:
|
||||||
|
en: Functions for calculating hashes of content.
|
||||||
|
it: Funzioni per calcolare hash di contenuti.
|
||||||
|
hash:
|
||||||
|
summary:
|
||||||
|
en: Responds with the hash-sum of the received message.
|
||||||
|
arguments:
|
||||||
|
algorithm:
|
||||||
|
en: Algorithm
|
||||||
|
it: Algoritmo
|
||||||
|
body:
|
||||||
|
en: Text to hash
|
||||||
|
it: Testo da hashare
|
||||||
|
|
@ -7,15 +7,15 @@
|
|||||||
def cHelp(context:EventContext, data:InputMessageData) -> None:
|
def cHelp(context:EventContext, data:InputMessageData) -> None:
|
||||||
moduleList = ''
|
moduleList = ''
|
||||||
for module in Modules:
|
for module in Modules:
|
||||||
summary = Modules[module]["summary"]
|
summary = Modules[module].summary
|
||||||
endpoints = Modules[module]["endpoints"]
|
endpoints = Modules[module].endpoints
|
||||||
moduleList += (f"\n\n{module}" + (f": {summary}" if summary else ''))
|
moduleList += (f"\n\n{module}" + (f": {summary}" if summary else ''))
|
||||||
for endpoint in endpoints:
|
for endpoint in endpoints:
|
||||||
summary = endpoints[endpoint]["summary"]
|
summary = endpoint.summary
|
||||||
moduleList += (f"\n* /{', /'.join(endpoints[endpoint]['names'])}" + (f": {summary}" if summary else ''))
|
moduleList += (f"\n* /{', /'.join(endpoint.names)}" + (f": {summary}" if summary else ''))
|
||||||
SendMessage(context, {"Text": f"[ Available Modules ]{moduleList}"})
|
SendMessage(context, {"Text": f"[ Available Modules ]{moduleList}"})
|
||||||
|
|
||||||
RegisterModule(name="Help", group="Basic", endpoints={
|
RegisterModule(name="Help", group="Basic", endpoints=[
|
||||||
"Help": CreateEndpoint(["help"], summary="Provides help for the bot. For now, it just lists the commands.", handler=cHelp),
|
SafeNamespace(names=["help"], summary="Provides help for the bot. For now, it just lists the commands.", handler=cHelp),
|
||||||
})
|
])
|
||||||
|
|
@ -137,14 +137,14 @@ def cSafebooru(context:EventContext, data:InputMessageData) -> None:
|
|||||||
except Exception as error:
|
except Exception as error:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
RegisterModule(name="Internet", summary="Tools and toys related to the Internet.", endpoints={
|
RegisterModule(name="Internet", summary="Tools and toys related to the Internet.", endpoints=[
|
||||||
"Embedded": CreateEndpoint(["embedded"], summary="Rewrites a link, trying to bypass embed view protection.", handler=cEmbedded),
|
SafeNamespace(names=["embedded"], summary="Rewrites a link, trying to bypass embed view protection.", handler=cEmbedded),
|
||||||
"Web": CreateEndpoint(["web"], summary="Provides results of a DuckDuckGo search.", handler=cWeb),
|
SafeNamespace(names=["web"], summary="Provides results of a DuckDuckGo search.", handler=cWeb),
|
||||||
"Translate": CreateEndpoint(["translate"], summary="Returns the received message after translating it in another language.", handler=cTranslate, arguments={
|
SafeNamespace(names=["translate"], summary="Returns the received message after translating it in another language.", handler=cTranslate, arguments={
|
||||||
"language_to": True,
|
"language_to": True,
|
||||||
"language_from": False,
|
"language_from": False,
|
||||||
}),
|
}),
|
||||||
"Unsplash": CreateEndpoint(["unsplash"], summary="Sends a picture sourced from Unsplash.", handler=cUnsplash),
|
SafeNamespace(names=["unsplash"], summary="Sends a picture sourced from Unsplash.", handler=cUnsplash),
|
||||||
"Safebooru": CreateEndpoint(["safebooru"], summary="Sends a picture sourced from Safebooru.", handler=cSafebooru),
|
SafeNamespace(names=["safebooru"], summary="Sends a picture sourced from Safebooru.", handler=cSafebooru),
|
||||||
})
|
])
|
||||||
|
|
||||||
|
@ -1,108 +0,0 @@
|
|||||||
# ==================================== #
|
|
||||||
# WinDog multi-purpose chatbot #
|
|
||||||
# Licensed under AGPLv3 by OctoSpacc #
|
|
||||||
# ==================================== #
|
|
||||||
|
|
||||||
import re, subprocess
|
|
||||||
|
|
||||||
def mPercenter(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
SendMessage(context, {"Text": choice(Locale.__(f'{data.Name}.{"done" if data.Body else "empty"}')).format(
|
|
||||||
Cmd=data.Tokens[0], Percent=RandPercent(), Thing=data.Body)})
|
|
||||||
|
|
||||||
def mMultifun(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
cmdkey = data.Name
|
|
||||||
replyToId = None
|
|
||||||
if data.Quoted:
|
|
||||||
replyFromUid = data.Quoted.User.Id
|
|
||||||
# TODO work on all platforms for the bot id
|
|
||||||
if replyFromUid.split('@')[0] == 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.messageId
|
|
||||||
else:
|
|
||||||
if 'empty' in Locale.__(cmdkey):
|
|
||||||
Text = choice(Locale.__(f'{cmdkey}.empty'))
|
|
||||||
SendMessage(context, {"Text": Text, "ReplyTo": replyToId})
|
|
||||||
|
|
||||||
def cStart(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
SendMessage(context, {"Text": choice(Locale.__('start')).format(data.User.Name)})
|
|
||||||
|
|
||||||
def cSource(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
SendMessage(context, {"TextPlain": ("""\
|
|
||||||
* Original Code: {https://gitlab.com/octospacc/WinDog}
|
|
||||||
* Mirror: {https://github.com/octospacc/WinDog}
|
|
||||||
""" + (f"* Modified Code: {{{ModifiedSourceUrl}}}" if ModifiedSourceUrl else ""))})
|
|
||||||
|
|
||||||
def cGdpr(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Module: Config
|
|
||||||
# ...
|
|
||||||
#def cConfig(update:telegram.Update, context:CallbackContext) -> None:
|
|
||||||
# Cmd = TelegramHandleCmd(update)
|
|
||||||
# if not Cmd: return
|
|
||||||
# # ... area: eu, us, ...
|
|
||||||
# # ... language: en, it, ...
|
|
||||||
# # ... userdata: import, export, delete
|
|
||||||
|
|
||||||
def cPing(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
SendMessage(context, {"Text": "*Pong!*"})
|
|
||||||
|
|
||||||
def cEcho(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
if data.command.body:
|
|
||||||
prefix = "🗣️ "
|
|
||||||
#prefix = f"[🗣️]({context.linker(data).message}) "
|
|
||||||
if len(data.Tokens) == 2:
|
|
||||||
nonascii = True
|
|
||||||
for char in data.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)
|
|
||||||
prefix = ''
|
|
||||||
SendMessage(context, {"Text": (prefix + data.command.body)})
|
|
||||||
else:
|
|
||||||
SendMessage(context, {"Text": choice(Locale.__('echo.empty'))})
|
|
||||||
|
|
||||||
#def cTime(update:Update, context:CallbackContext) -> None:
|
|
||||||
# update.message.reply_markdown_v2(
|
|
||||||
# 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 cExec(context:EventContext, data:InputMessageData) -> None:
|
|
||||||
if len(data.Tokens) >= 2 and data.Tokens[1].lower() in ExecAllowed:
|
|
||||||
cmd = data.Tokens[1].lower()
|
|
||||||
Out = subprocess.run(('sh', '-c', f'export PATH=$PATH:/usr/games; {cmd}'),
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode()
|
|
||||||
# <https://stackoverflow.com/a/14693789>
|
|
||||||
Caption = (re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])').sub('', Out))
|
|
||||||
SendMessage(context, {
|
|
||||||
"TextPlain": Caption,
|
|
||||||
"TextMarkdown": MarkdownCode(Caption, True),
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
SendMessage(context, {"Text": choice(Locale.__('eval'))})
|
|
||||||
|
|
||||||
RegisterModule(name="Misc", endpoints={
|
|
||||||
"Percenter": CreateEndpoint(["wish", "level"], summary="Provides fun trough percentage-based toys.", handler=mPercenter),
|
|
||||||
"Multifun": CreateEndpoint(["hug", "pat", "poke", "cuddle", "hands", "floor", "sessocto"], summary="Provides fun trough preprogrammed-text-based toys.", handler=mMultifun),
|
|
||||||
"Start": CreateEndpoint(["start"], summary="Salutes the user, hinting that the bot is working and providing basic quick help.", handler=cStart),
|
|
||||||
"Source": CreateEndpoint(["source"], summary="Provides a copy of the bot source codes and/or instructions on how to get it.", handler=cSource),
|
|
||||||
"GDPR": CreateEndpoint(["gdpr"], summary="Operations for european citizens regarding your personal data.", handler=cGdpr),
|
|
||||||
"Ping": CreateEndpoint(["ping"], summary="Responds pong, useful for testing messaging latency.", handler=cPing),
|
|
||||||
"Echo": CreateEndpoint(["echo"], summary="Responds back with the original text of the received message.", handler=cEcho),
|
|
||||||
"Eval": CreateEndpoint(["eval"], summary="Execute a Python command (or safe literal operation) in the current context. Currently not implemented.", handler=cEval),
|
|
||||||
"Exec": CreateEndpoint(["exec"], summary="Execute a system command from the allowed ones and return stdout+stderr.", handler=cExec),
|
|
||||||
#"Format": CreateEndpoint(["format"], summary="Reformat text using an handful of rules. Not yet implemented.", handler=cFormat),
|
|
||||||
#"Frame": CreateEndpoint(["frame"], summary="Frame someone's message into a platform-styled image. Not yet implemented.", handler=cFrame),
|
|
||||||
#"Repeat": CreateEndpoint(["repeat"], summary="I had this planned but I don't remember what this should have done. Not yet implemented.", handler=cRepeat),
|
|
||||||
})
|
|
||||||
|
|
28
ModWinDog/Multifun/Multifun.py
Normal file
28
ModWinDog/Multifun/Multifun.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# ==================================== #
|
||||||
|
# WinDog multi-purpose chatbot #
|
||||||
|
# Licensed under AGPLv3 by OctoSpacc #
|
||||||
|
# ==================================== #
|
||||||
|
|
||||||
|
def mMultifun(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
cmdkey = data.Name
|
||||||
|
replyToId = None
|
||||||
|
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.messageId
|
||||||
|
else:
|
||||||
|
if 'empty' in Locale.__(cmdkey):
|
||||||
|
Text = choice(Locale.__(f'{cmdkey}.empty'))
|
||||||
|
SendMessage(context, {"Text": Text, "ReplyTo": replyToId})
|
||||||
|
|
||||||
|
RegisterModule(name="Multifun", endpoints=[
|
||||||
|
SafeNamespace(names=["hug", "pat", "poke", "cuddle", "hands", "floor", "sessocto"], summary="Provides fun trough preprogrammed-text-based toys.", handler=mMultifun),
|
||||||
|
])
|
||||||
|
|
13
ModWinDog/Percenter/Percenter.py
Normal file
13
ModWinDog/Percenter/Percenter.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# ==================================== #
|
||||||
|
# WinDog multi-purpose chatbot #
|
||||||
|
# Licensed under AGPLv3 by OctoSpacc #
|
||||||
|
# ==================================== #
|
||||||
|
|
||||||
|
def mPercenter(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
SendMessage(context, {"Text": choice(Locale.__(f'{data.Name}.{"done" if data.Body else "empty"}')).format(
|
||||||
|
Cmd=data.Tokens[0], Percent=RandPercent(), Thing=data.Body)})
|
||||||
|
|
||||||
|
RegisterModule(name="Percenter", endpoints=[
|
||||||
|
SafeNamespace(names=["wish", "level"], summary="Provides fun trough percentage-based toys.", handler=mPercenter),
|
||||||
|
])
|
||||||
|
|
@ -65,7 +65,7 @@ def cDalleSelenium(context:EventContext, data:InputMessageData) -> None:
|
|||||||
if not len(img_list):
|
if not len(img_list):
|
||||||
try:
|
try:
|
||||||
driver.find_element('img.gil_err_img[alt="Unsafe image content detected"]')
|
driver.find_element('img.gil_err_img[alt="Unsafe image content detected"]')
|
||||||
SendMessage(context, {"Text": "Unsafe image content detected: This result {warning_text}", "media": {"bytes": open("./Assets/ImageCreator-CodeOfConduct.png", 'rb').read()}})
|
SendMessage(context, {"Text": f"Unsafe image content detected: This result {warning_text}", "media": {"bytes": open("./Assets/ImageCreator-CodeOfConduct.png", 'rb').read()}})
|
||||||
return closeSelenium(driver_index, driver)
|
return closeSelenium(driver_index, driver)
|
||||||
except: # no error is present, so we just have to wait more for the images
|
except: # no error is present, so we just have to wait more for the images
|
||||||
continue
|
continue
|
||||||
@ -121,8 +121,8 @@ def cCraiyonSelenium(context:EventContext, data:InputMessageData) -> None:
|
|||||||
SendMessage(context, {"TextPlain": "An unexpected error occurred."})
|
SendMessage(context, {"TextPlain": "An unexpected error occurred."})
|
||||||
closeSelenium(driver_index, driver)
|
closeSelenium(driver_index, driver)
|
||||||
|
|
||||||
RegisterModule(name="Scrapers", endpoints={
|
RegisterModule(name="Scrapers", endpoints=[
|
||||||
"DALL-E": CreateEndpoint(["dalle"], summary="Sends an AI-generated picture from DALL-E 3 via Microsoft Bing.", handler=cDalleSelenium),
|
SafeNamespace(names=["dalle"], summary="Sends an AI-generated picture from DALL-E 3 via Microsoft Bing.", handler=cDalleSelenium),
|
||||||
"Craiyon": CreateEndpoint(["craiyon"], summary="Sends an AI-generated picture from Craiyon.com.", handler=cCraiyonSelenium),
|
SafeNamespace(names=["craiyon"], summary="Sends an AI-generated picture from Craiyon.com.", handler=cCraiyonSelenium),
|
||||||
})
|
])
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ end)()"""))
|
|||||||
Log(textOutput := ("Lua Error: " + str(error)))
|
Log(textOutput := ("Lua Error: " + str(error)))
|
||||||
SendMessage(context, {"TextPlain": textOutput})
|
SendMessage(context, {"TextPlain": textOutput})
|
||||||
|
|
||||||
RegisterModule(name="Scripting", group="Geek", summary="Tools for programming the bot and expanding its features.", endpoints={
|
RegisterModule(name="Scripting", group="Geek", summary="Tools for programming the bot and expanding its features.", endpoints=[
|
||||||
"Lua": CreateEndpoint(["lua"], summary="Execute a Lua snippet and get its output.", handler=cLua),
|
SafeNamespace(names=["lua"], summary="Execute a Lua snippet and get its output.", handler=cLua),
|
||||||
})
|
])
|
||||||
|
|
||||||
|
146
WinDog.py
146
WinDog.py
@ -7,12 +7,14 @@
|
|||||||
import json, time
|
import json, time
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from magic import Magic
|
from hashlib import new as hashlib_new
|
||||||
from os import listdir
|
from os import listdir
|
||||||
from os.path import isfile, isdir
|
from os.path import isfile, isdir
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
from yaml import load as yaml_load, BaseLoader as yaml_BaseLoader
|
||||||
|
#from xml.etree import ElementTree
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from html import unescape as HtmlUnescape
|
from html import unescape as HtmlUnescape
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
@ -38,6 +40,13 @@ class InputMessageData(SafeNamespace):
|
|||||||
class OutputMessageData(SafeNamespace):
|
class OutputMessageData(SafeNamespace):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def NamespaceUnion(namespaces:list|tuple, clazz=SimpleNamespace):
|
||||||
|
dikt = {}
|
||||||
|
for namespace in namespaces:
|
||||||
|
for key, value in tuple(namespace.__dict__.items()):
|
||||||
|
dikt[key] = value
|
||||||
|
return 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'
|
||||||
if newline == False or (inline and newline == None):
|
if newline == False or (inline and newline == None):
|
||||||
@ -90,11 +99,39 @@ def InDict(dikt:dict, key:str, /) -> any:
|
|||||||
def DictGet(dikt:dict, key:str, /) -> any:
|
def DictGet(dikt:dict, key:str, /) -> any:
|
||||||
return (dikt[key] if key in dikt else None)
|
return (dikt[key] if key in dikt else None)
|
||||||
|
|
||||||
|
def ObjGet(node:object, query:str, /) -> any:
|
||||||
|
for key in query.split('.'):
|
||||||
|
if hasattr(node, "__getitem__") and node.__getitem__:
|
||||||
|
# dicts and such
|
||||||
|
method = "__getitem__"
|
||||||
|
exception = KeyError
|
||||||
|
else:
|
||||||
|
# namespaces and such
|
||||||
|
method = "__getattribute__"
|
||||||
|
exception = AttributeError
|
||||||
|
try:
|
||||||
|
node = node.__getattribute__(method)(key)
|
||||||
|
except exception:
|
||||||
|
return None
|
||||||
|
#try:
|
||||||
|
# node = node[key]
|
||||||
|
#except TypeError:
|
||||||
|
# node = node.__getattribute__(key)
|
||||||
|
#except (TypeError, KeyError, AttributeError):
|
||||||
|
# return None
|
||||||
|
return node
|
||||||
|
|
||||||
def isinstanceSafe(clazz:any, instance:any) -> bool:
|
def isinstanceSafe(clazz:any, instance:any) -> bool:
|
||||||
if instance != None:
|
if instance != None:
|
||||||
return isinstance(clazz, instance)
|
return isinstance(clazz, instance)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def GetString(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}.en")):
|
||||||
|
result = tuple(ObjGet(bank, query).values())[0]
|
||||||
|
return result
|
||||||
|
|
||||||
def CharEscape(String:str, Escape:str='') -> str:
|
def CharEscape(String:str, Escape:str='') -> str:
|
||||||
if Escape == 'MARKDOWN':
|
if Escape == 'MARKDOWN':
|
||||||
return escape_markdown(String, version=2)
|
return escape_markdown(String, version=2)
|
||||||
@ -159,22 +196,28 @@ def RandHexStr(length:int) -> str:
|
|||||||
hexa += choice('0123456789abcdef')
|
hexa += choice('0123456789abcdef')
|
||||||
return hexa
|
return hexa
|
||||||
|
|
||||||
|
def GetUserData(user_id:str) -> SafeNamespace|None:
|
||||||
|
try:
|
||||||
|
return User.get(User.id == user_id)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
def ParseCommand(text:str) -> SafeNamespace|None:
|
def ParseCommand(text:str) -> SafeNamespace|None:
|
||||||
command = SafeNamespace()
|
|
||||||
if not text:
|
if not text:
|
||||||
return command
|
return None
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
try: # ensure text is a non-empty command
|
try: # ensure text is a non-empty command
|
||||||
if not (text[0] in CmdPrefixes and text[1:].strip()):
|
if not (text[0] in CmdPrefixes and text[1:].strip()):
|
||||||
return command
|
return None
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return
|
return None
|
||||||
|
command = SafeNamespace()
|
||||||
command.tokens = text.replace("\r", " ").replace("\n", " ").replace("\t", " ").replace(" ", " ").replace(" ", " ").split(" ")
|
command.tokens = text.replace("\r", " ").replace("\n", " ").replace("\t", " ").replace(" ", " ").replace(" ", " ").split(" ")
|
||||||
command.name = command.tokens[0][1:].lower()
|
command.name = command.tokens[0][1:].lower()
|
||||||
command.body = text[len(command.tokens[0]):].strip()
|
command.body = text[len(command.tokens[0]):].strip()
|
||||||
if command.name not in Endpoints:
|
if command.name not in Endpoints:
|
||||||
return command
|
return command
|
||||||
if (endpoint_arguments := Endpoints[command.name]["arguments"]):
|
if (endpoint_arguments := Endpoints[command.name].arguments):#["arguments"]):
|
||||||
command.arguments = {}
|
command.arguments = {}
|
||||||
index = 1
|
index = 1
|
||||||
for key in endpoint_arguments:
|
for key in endpoint_arguments:
|
||||||
@ -190,20 +233,31 @@ def ParseCommand(text:str) -> SafeNamespace|None:
|
|||||||
return command
|
return command
|
||||||
|
|
||||||
def OnMessageParsed(data:InputMessageData) -> None:
|
def OnMessageParsed(data:InputMessageData) -> None:
|
||||||
if Debug and (DumpToFile or DumpToConsole):
|
DumpMessage(data)
|
||||||
text = (data.text_auto.replace('\n', '\\n') if data.text_auto else '')
|
UpdateUserDb(data.user)
|
||||||
text = f"[{int(time.time())}] [{time.ctime()}] [{data.room.id}] [{data.message_id}] [{data.user.id}] {text}"
|
|
||||||
if DumpToConsole:
|
|
||||||
print(text)
|
|
||||||
if DumpToFile:
|
|
||||||
open((DumpToFile if (DumpToFile and type(DumpToFile) == str) else "./Dump.txt"), 'a').write(text + '\n')
|
|
||||||
|
|
||||||
def SendMessage(context, data:OutputMessageData, destination=None) -> None:
|
def UpdateUserDb(user:SafeNamespace) -> None:
|
||||||
if type(context) == dict:
|
try:
|
||||||
event = context['Event'] if 'Event' in context else None
|
User.get(User.id == user.id)
|
||||||
manager = context['Manager'] if 'Manager' in context else None
|
except User.DoesNotExist:
|
||||||
else:
|
user_hash = ("sha256:" + hashlib_new("sha256", user.id.encode()).hexdigest())
|
||||||
[event, manager] = [context, context]
|
try:
|
||||||
|
User.get(User.id_hash == user_hash)
|
||||||
|
User.update(id=user.id).where(User.id_hash == user_hash)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
User.create(id=user.id, id_hash=user_hash)
|
||||||
|
|
||||||
|
def DumpMessage(data:InputMessageData) -> None:
|
||||||
|
if not (Debug and (DumpToFile or DumpToConsole)):
|
||||||
|
return
|
||||||
|
text = (data.text_auto.replace('\n', '\\n') if data.text_auto else '')
|
||||||
|
text = f"[{int(time.time())}] [{time.ctime()}] [{data.room.id}] [{data.message_id}] [{data.user.id}] {text}"
|
||||||
|
if DumpToConsole:
|
||||||
|
print(text, data)
|
||||||
|
if DumpToFile:
|
||||||
|
open((DumpToFile if (DumpToFile and type(DumpToFile) == str) else "./Dump.txt"), 'a').write(text + '\n')
|
||||||
|
|
||||||
|
def SendMessage(context:EventContext, data:OutputMessageData, destination=None) -> None:
|
||||||
if InDict(data, 'TextPlain') or InDict(data, 'TextMarkdown'):
|
if InDict(data, 'TextPlain') or InDict(data, 'TextMarkdown'):
|
||||||
textPlain = InDict(data, 'TextPlain')
|
textPlain = InDict(data, 'TextPlain')
|
||||||
textMarkdown = InDict(data, 'TextMarkdown')
|
textMarkdown = InDict(data, 'TextMarkdown')
|
||||||
@ -215,27 +269,62 @@ def SendMessage(context, data:OutputMessageData, destination=None) -> None:
|
|||||||
textMarkdown = CharEscape(HtmlUnescape(data['Text']), InferMdEscape(HtmlUnescape(data['Text']), textPlain))
|
textMarkdown = CharEscape(HtmlUnescape(data['Text']), InferMdEscape(HtmlUnescape(data['Text']), textPlain))
|
||||||
for platform in Platforms:
|
for platform in Platforms:
|
||||||
platform = Platforms[platform]
|
platform = Platforms[platform]
|
||||||
if isinstanceSafe(event, platform.eventClass) or isinstanceSafe(manager, platform.managerClass):
|
if isinstanceSafe(context.event, platform.eventClass) or isinstanceSafe(context.manager, platform.managerClass):
|
||||||
platform.sender(event, manager, data, destination, textPlain, textMarkdown)
|
return platform.sender(context, data, destination, textPlain, textMarkdown)
|
||||||
|
|
||||||
def SendNotice(context, data) -> None:
|
def SendNotice(context:EventContext, data) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def DeleteMessage(context:EventContext, data) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
#def ParseModuleTree(module:ElementTree.Element):
|
||||||
|
# def parseTexts(tree:ElementTree.Element):
|
||||||
|
# texts = {}
|
||||||
|
# for text in tree:
|
||||||
|
# texts[text.tag] = text.text
|
||||||
|
# return texts
|
||||||
|
# if module.tag != "module":
|
||||||
|
# raise Exception(f"Unknown root element <{module.tag}> in {FILE}; it should be <module>.")
|
||||||
|
# for option in module:
|
||||||
|
# match option.tag:
|
||||||
|
# case _:
|
||||||
|
# parseTexts(option)
|
||||||
|
# case "endpoints":
|
||||||
|
# for endpoint in option:
|
||||||
|
# for option in endpoint:
|
||||||
|
# match option.tag:
|
||||||
|
# case _:
|
||||||
|
# parseTexts(option)
|
||||||
|
# case "arguments":
|
||||||
|
# for argument in option:
|
||||||
|
# parseTexts(argument)
|
||||||
|
|
||||||
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, *, eventClass=None, managerClass=None) -> None:
|
||||||
Platforms[name] = SafeNamespace(main=main, sender=sender, linker=linker, eventClass=eventClass, managerClass=managerClass)
|
Platforms[name] = SafeNamespace(main=main, sender=sender, linker=linker, eventClass=eventClass, managerClass=managerClass)
|
||||||
Log(f"{name}, ", inline=True)
|
Log(f"{name}, ", inline=True)
|
||||||
|
|
||||||
def RegisterModule(name:str, endpoints:dict, *, group:str|None=None, summary:str|None=None) -> None:
|
def RegisterModule(name:str, endpoints:dict, *, group:str|None=None, summary:str|None=None) -> None:
|
||||||
Modules[name] = {"group": group, "summary": summary, "endpoints": endpoints}
|
module = SafeNamespace(group=group, endpoints=(SafeNamespace(**endpoints) if type(endpoints) == dict else endpoints))
|
||||||
|
# TODO load XML data, add to both module and locale objects
|
||||||
|
#if isfile(file := f"./ModWinDog/{name}/{name}.xml"):
|
||||||
|
# ParseModuleTree(ElementTree.parse(file).getroot())
|
||||||
|
if isfile(file := f"./ModWinDog/{name}/{name}.yaml"):
|
||||||
|
module.strings = yaml_load(open(file, 'r').read().replace("\t", " "), Loader=yaml_BaseLoader)
|
||||||
|
module.get_string = (lambda query, lang=None: GetString(module.strings, query, lang))
|
||||||
|
Modules[name] = module
|
||||||
Log(f"{name}, ", inline=True)
|
Log(f"{name}, ", inline=True)
|
||||||
for endpoint in endpoints:
|
for endpoint in endpoints:
|
||||||
endpoint = endpoints[endpoint]
|
endpoint.module = module
|
||||||
for name in endpoint["names"]:
|
endpoint.get_string = (lambda query, lang=None: module.get_string(f"endpoints.{endpoint.names[0]}.{query}", lang))
|
||||||
|
for name in endpoint.names:
|
||||||
Endpoints[name] = endpoint
|
Endpoints[name] = endpoint
|
||||||
|
|
||||||
# TODO register endpoint with this instead of RegisterModule
|
def CallEndpoint(name:str, context:EventContext, data:InputMessageData):
|
||||||
def CreateEndpoint(names:list[str], handler:callable, arguments:dict[str, bool]|None=None, *, summary:str|None=None) -> dict:
|
endpoint = Endpoints[name]
|
||||||
return {"names": names, "summary": summary, "handler": handler, "arguments": arguments}
|
context.endpoint = endpoint
|
||||||
|
context.module = endpoint.module
|
||||||
|
return endpoint.handler(context, data)
|
||||||
|
|
||||||
def WriteNewConfig() -> None:
|
def WriteNewConfig() -> None:
|
||||||
Log("💾️ No configuration found! Generating and writing to `./Config.py`... ", inline=True)
|
Log("💾️ No configuration found! Generating and writing to `./Config.py`... ", inline=True)
|
||||||
@ -296,7 +385,6 @@ if __name__ == '__main__':
|
|||||||
Log("...Done. ✅️", inline=True, newline=True)
|
Log("...Done. ✅️", inline=True, newline=True)
|
||||||
|
|
||||||
Log("💽️ Loading Configuration... ", newline=False)
|
Log("💽️ Loading Configuration... ", newline=False)
|
||||||
#exec(open("./LibWinDog/Config.py", 'r').read())
|
|
||||||
from Config import *
|
from Config import *
|
||||||
if isfile("./Config.py"):
|
if isfile("./Config.py"):
|
||||||
from Config import *
|
from Config import *
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
Markdown
|
Markdown
|
||||||
peewee
|
peewee
|
||||||
|
PyYAML
|
||||||
|
Reference in New Issue
Block a user