Archived
1
0
Fork 0
This repository has been archived on 2024-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
akari-bot/bot.py

180 lines
5.6 KiB
Python
Raw Normal View History

2022-06-12 07:07:53 +00:00
import os
import shutil
2021-08-07 12:55:07 +00:00
import subprocess
2022-07-01 06:26:41 +00:00
import sys
2021-08-07 12:55:07 +00:00
from queue import Queue, Empty
from threading import Thread
2021-08-25 12:05:57 +00:00
from time import sleep
2021-08-21 07:45:21 +00:00
2022-01-20 12:13:03 +00:00
import psutil
2022-07-31 08:27:58 +00:00
from loguru import logger
2022-01-20 12:13:03 +00:00
2022-01-20 13:44:54 +00:00
from config import Config
2022-06-13 12:01:53 +00:00
from database import BotDBUtil, session, DBVersion
2021-08-23 12:44:31 +00:00
2021-08-21 07:45:21 +00:00
encode = 'UTF-8'
2020-09-19 10:35:13 +00:00
2023-08-29 16:43:13 +00:00
bots_required_configs = {'aiocqhttp': ['qq_host', 'qq_account'], 'discord': ['dc_token'], 'aiogram': ['tg_token'],
2023-11-06 13:10:11 +00:00
'kook': ['kook_token'], 'matrix': ['matrix_homeserver', 'matrix_user', 'matrix_token'],
'lagrange': ['lagrange_host']}
2022-01-13 15:50:17 +00:00
class RestartBot(Exception):
pass
def get_pid(name):
return [p.pid for p in psutil.process_iter() if p.name().find(name) != -1]
2021-08-07 12:55:07 +00:00
def enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(line)
out.close()
2021-02-01 15:13:11 +00:00
2021-08-21 15:58:07 +00:00
2022-01-20 13:44:54 +00:00
def init_bot():
base_superuser = Config('base_superuser')
if base_superuser is not None:
if isinstance(base_superuser, str):
base_superuser = [base_superuser]
for bu in base_superuser:
BotDBUtil.SenderInfo(bu).edit('isSuperUser', True)
2021-03-05 16:19:06 +00:00
2021-08-20 16:32:46 +00:00
2022-01-13 15:50:17 +00:00
pidlst = []
2023-11-01 02:50:28 +00:00
disabled_bots = Config('disabled_bots', [])
2023-07-12 15:48:08 +00:00
2022-01-13 15:50:17 +00:00
def run_bot():
2023-05-28 06:40:01 +00:00
cache_path = os.path.abspath(Config('cache_path'))
if os.path.exists(cache_path):
shutil.rmtree(cache_path)
os.mkdir(cache_path)
else:
os.mkdir(cache_path)
2022-01-13 15:50:17 +00:00
pid_cache = os.path.abspath('.pid_last')
if os.path.exists(pid_cache):
with open(pid_cache, 'r') as f:
pid_last = f.read().split('\n')
running_pids = get_pid('python')
for pid in pid_last:
if int(pid) in running_pids:
try:
os.kill(int(pid), 9)
2022-01-13 16:01:29 +00:00
except (PermissionError, ProcessLookupError):
2022-01-13 15:50:17 +00:00
pass
os.remove(pid_cache)
2022-01-20 14:03:07 +00:00
envs = os.environ.copy()
envs['PYTHONIOENCODING'] = 'UTF-8'
envs['PYTHONPATH'] = os.path.abspath('.')
2022-06-12 07:07:53 +00:00
botdir = './bots/'
2022-01-13 15:50:17 +00:00
lst = os.listdir(botdir)
runlst = []
2023-07-12 15:48:08 +00:00
for bl in lst:
if bl in disabled_bots:
continue
2023-08-29 16:43:13 +00:00
if bl in bots_required_configs:
abort = False
for c in bots_required_configs[bl]:
if not Config(c):
logger.error(f'Bot {bl} requires config {c} but not found, abort to launch.')
abort = True
break
if abort:
continue
2023-07-12 15:48:08 +00:00
bot = os.path.abspath(f'{botdir}{bl}/bot.py')
2022-01-13 15:50:17 +00:00
if os.path.exists(bot):
2023-09-01 11:42:33 +00:00
p = subprocess.Popen([sys.executable, bot, 'subprocess'], shell=False, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
2022-01-20 14:03:07 +00:00
cwd=os.path.abspath('.'), env=envs)
2022-01-13 15:50:17 +00:00
runlst.append(p)
pidlst.append(p.pid)
with open(pid_cache, 'w') as c:
c.write('\n'.join(str(p) for p in pidlst))
q = Queue()
threads = []
for p in runlst:
threads.append(Thread(target=enqueue_output, args=(p.stdout, q)))
for t in threads:
t.daemon = True
t.start()
2021-08-25 12:05:57 +00:00
2021-12-25 07:58:24 +00:00
while True:
2021-08-07 12:55:07 +00:00
try:
line = q.get_nowait()
2021-12-25 07:58:24 +00:00
except Empty:
2023-04-20 22:34:51 +00:00
sleep(1)
2021-12-25 07:58:24 +00:00
else:
try:
2022-08-25 13:48:34 +00:00
logger.info(line.decode(encode)[:-1])
except UnicodeDecodeError:
encode_list = ['GBK']
for e in encode_list:
try:
logger.warning(f'Cannot decode string from UTF-8, decode with {e}: '
+ line.decode(e)[:-1])
break
except Exception:
2022-08-25 13:50:58 +00:00
if encode_list[-1] != e:
logger.warning(f'Cannot decode string from {e}, '
f'attempting with {encode_list[encode_list.index(e) + 1]}.')
else:
logger.error(f'Cannot decode string from {e}, no more attempts.')
2021-08-07 12:55:07 +00:00
2022-01-13 15:50:17 +00:00
for p in runlst:
if p.poll() == 233:
2022-07-01 06:26:41 +00:00
logger.warning(f'{p.pid} exited with code 233, restart all bots.')
2022-01-13 15:50:17 +00:00
pidlst.remove(p.pid)
raise RestartBot
2023-07-12 15:48:08 +00:00
# break when all processes are done.
if all(p.poll() is not None for p in runlst):
break
2023-04-30 16:14:59 +00:00
sleep(0.0001)
2022-01-13 15:50:17 +00:00
2022-01-20 13:44:54 +00:00
if __name__ == '__main__':
init_bot()
2022-07-01 06:26:41 +00:00
logger.remove()
logger.add(sys.stderr, format='{message}', level="INFO")
2022-08-27 07:38:13 +00:00
query_dbver = session.query(DBVersion).first()
if query_dbver is None:
2023-04-12 06:29:21 +00:00
session.add_all([DBVersion(value=str(BotDBUtil.database_version))])
2022-06-13 12:01:53 +00:00
session.commit()
2022-08-27 07:38:13 +00:00
query_dbver = session.query(DBVersion).first()
if (current_ver := int(query_dbver.value)) < (target_ver := BotDBUtil.database_version):
logger.info(f'Updating database from {current_ver} to {target_ver}...')
from database.update import update_database
2023-01-28 05:53:11 +00:00
2022-08-27 07:38:13 +00:00
update_database()
2022-08-27 07:57:11 +00:00
logger.info('Database updated successfully!')
2022-01-20 13:44:54 +00:00
try:
while True:
try:
run_bot() # Process will block here so
2022-07-01 06:26:41 +00:00
logger.error('All bots exited unexpectedly, please check the output')
2022-01-20 13:44:54 +00:00
break
except RestartBot:
for x in pidlst:
try:
os.kill(x, 9)
except (PermissionError, ProcessLookupError):
pass
pidlst.clear()
sleep(5)
continue
except KeyboardInterrupt:
for x in pidlst:
try:
os.kill(x, 9)
except (PermissionError, ProcessLookupError):
pass