162 lines
6.8 KiB
Python
162 lines
6.8 KiB
Python
# Copyright (C) 2022 nocturn9x
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
from pyrogram import Client, idle
|
|
from pyrogram.session import Session
|
|
import asyncio
|
|
import logging
|
|
import time
|
|
import uvloop
|
|
import sys
|
|
from configparser import ConfigParser
|
|
from UnoVRBot.misc.config import BotConfig
|
|
from pydantic import ValidationError
|
|
|
|
|
|
# Main Bot module. This is the entry point to the Bot and it loads all the modules and
|
|
# performs automatic startup, actually pyrogram does all the job but shut up we are communist
|
|
|
|
|
|
def log(prefix: str = "", message: str = ""):
|
|
"""
|
|
Hey I like logging but this is used only when no proper
|
|
logging configuration is yet in place to actually be useful
|
|
:param message: The message to log to stderr, default to ""
|
|
:type message: str
|
|
:param prefix: Any extra prefix to add to the message, default to ""
|
|
Use {date} to replace it with the current date in d/m/Y format and
|
|
{time} to replace it with the current time in %H:%M:%S %p format or
|
|
just %T %p if you're a lazy sucker. A whitespace is automatically
|
|
added between the prefix and the suffix just in case you're dumb
|
|
:type prefix: str
|
|
:return: Nothing
|
|
:rtype: None
|
|
"""
|
|
|
|
sys.stderr.write(f"{prefix.format(date=time.strftime('%d/%m/%Y'), time=time.strftime('%T %p'))} {message}\n")
|
|
|
|
|
|
async def start(config: BotConfig):
|
|
"""
|
|
Starts the Bot, or at least tries to do so and give
|
|
up after a while cuz we've got other shit to do than starting shitty
|
|
Bots all day
|
|
:param config: The BotConfig class (yeah, thanks, explanation be like)
|
|
:type config: BotConfig
|
|
:return: Nothing, fucker
|
|
:rtype: None
|
|
"""
|
|
|
|
log("[Bot - INFO {date} {time}]",
|
|
"Loading an actual logging configuration")
|
|
logging.basicConfig(format=config.logging_format,
|
|
datefmt=config.logging_date_format,
|
|
level=config.logging_level)
|
|
logging.info("Now we're talking, creating the Client instance")
|
|
Session.notice_displayed = True # Sorry but no one really cares
|
|
logging.getLogger("pyrogram").setLevel(logging.WARNING) # Because pyrogram logs are fucking verbose
|
|
client = Client(api_id=config.api_id,
|
|
api_hash=config.api_hash,
|
|
bot_token=config.bot_token,
|
|
plugins={"root": config.plugins_dir},
|
|
workdir=config.working_directory,
|
|
name=config.session_name)
|
|
try:
|
|
await client.start()
|
|
logging.info("Client started, goin' asleep baby")
|
|
await idle()
|
|
except Exception as oh_no:
|
|
logging.error(f"A fatal error occurred -> {type(oh_no).__name__}: {oh_no}, exiting")
|
|
try:
|
|
logging.debug("Disconnecting from telegram")
|
|
await client.disconnect()
|
|
except Exception as disconnect:
|
|
logging.debug(f"Could not disconnect -> {type(disconnect).__name__}: {disconnect}")
|
|
finally:
|
|
if config.restart_times:
|
|
logging.warning(f"Restarting up to {config.restart_times} more times")
|
|
config.restart_times -= 1
|
|
await start(config)
|
|
|
|
|
|
def check_config(parser: ConfigParser):
|
|
"""
|
|
Checks if a parsed .INI file actually contains the required settings
|
|
or if the user is a sack of shit. Starts the Bot if the user
|
|
is actually a good boy or beat him to death otherwise :)
|
|
:param parser: A ConfigParser instance (I should really consider to become a teacher, boy)
|
|
:type parser: ConfigParser
|
|
:return: Again, not the slightest shit
|
|
:rtype: None
|
|
"""
|
|
|
|
getters = { # Sort of like a computed goto. Kind of, I just hate a ton of if conditions
|
|
"api_id": (ConfigParser.getint, "int"),
|
|
"api_hash": (ConfigParser.get, "str"),
|
|
"logging_format": (ConfigParser.get, "int"),
|
|
"logging_level": (ConfigParser.getint, "str"),
|
|
"logging_date_format": (ConfigParser.get, "str"),
|
|
"plugins_dir": (ConfigParser.get, "str"),
|
|
"working_directory": (ConfigParser.get, "str"),
|
|
"session_name": (ConfigParser.get, "str"),
|
|
"restart_times": (ConfigParser.getint, "int"),
|
|
"bot_token": (ConfigParser.get, "str")
|
|
}
|
|
options = {}
|
|
if "bot" not in parser:
|
|
log("[Bot - ERROR {date} {time}]",
|
|
"How am I gonna start if you can't even fucking fill a config file? Missing 'bot' section")
|
|
return
|
|
for option in parser["bot"]:
|
|
getter, expected = getters.get(option, (None, None))
|
|
if not getter:
|
|
log("[Bot - WARN {date} {time}]",
|
|
f"Unknown option '{option}', skipping it, idiot!")
|
|
continue
|
|
try:
|
|
options[option] = getter(parser, "bot", option)
|
|
except Exception as fucked_up:
|
|
log("[Bot - ERROR {date} {time}]",
|
|
f"Hey! Got a type mismatch for option '{option}' (expecting '{expected}'), can you please stop being a dumbass? -> {type(fucked_up).__name__}: {fucked_up}")
|
|
try:
|
|
asyncio.run(start(BotConfig(**options)))
|
|
except AttributeError: # Python 3.6
|
|
asyncio.get_event_loop().run_until_complete(start(BotConfig(**options)))
|
|
except ValidationError as validation_error:
|
|
log("[Bot - ERROR {date} {time}]",
|
|
f"What the heck did you do? Apparently some type mismatch occurred -> {validation_error}")
|
|
|
|
|
|
if __name__ == "__main__": # Start dat shit boi
|
|
uvloop.install()
|
|
parser = ConfigParser()
|
|
if len(sys.argv) < 2:
|
|
# Yeah yeah say what you want fucker, but how am I gonna log without proper config in place? :\
|
|
# Take this shitty print instead!
|
|
log("[Bot - INFO {date} {time}]",
|
|
"Loading config file at default path ('./config.ini'), were you too lazy to specify a custom one?")
|
|
file = "./config.ini"
|
|
else:
|
|
log("[Bot - INFO {date} {time}]",
|
|
f"I see you were not lazy! Loading config file at '{sys.argv[1]}'")
|
|
file = sys.argv[1]
|
|
try:
|
|
with open(file) as config:
|
|
parser.read_file(config)
|
|
except Exception as fuck: # Cause we're too lazy for anything else
|
|
log("[Bot - ERROR {date} {time}]",
|
|
f"Sometimes thing go well, sometimes they don't, fix this shit fucker -> {type(fuck.__name__)}: {fuck}")
|
|
else:
|
|
log("[Bot - INFO {date} {time}]",
|
|
"Config file loaded, let's see if you're retarded or not now, checking options")
|
|
check_config(parser)
|