237 lines
6.9 KiB
Python
237 lines
6.9 KiB
Python
import asyncio
|
|
import datetime
|
|
import json
|
|
import logging
|
|
import os
|
|
import sys
|
|
from json import JSONDecodeError
|
|
from typing import Union
|
|
|
|
import discord
|
|
import requests
|
|
from discord.ext import commands
|
|
from dotenv import load_dotenv
|
|
|
|
from db import DiscordDB
|
|
|
|
APP_NAME = "discord_bot"
|
|
|
|
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
|
|
load_dotenv()
|
|
logging.basicConfig(level=logging.WARNING, filename="logging.log", format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
|
logger = logging.getLogger(APP_NAME)
|
|
logger.setLevel(logging.DEBUG)
|
|
logger.propagate = False
|
|
fh = logging.FileHandler(f"./logging.log", "w")
|
|
fh.setLevel(logging.DEBUG)
|
|
logger.addHandler(fh)
|
|
keep_fds = [fh.stream.fileno()]
|
|
|
|
os.makedirs("debug", exist_ok=True)
|
|
|
|
pidfile = "pid"
|
|
with open(pidfile, "w") as f:
|
|
f.write(str(os.getpid()))
|
|
|
|
DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")
|
|
DEFAULT_CHANNEL_ID = os.getenv("DEFAULT_CHANNEL_ID", 603527159109124096)
|
|
ADMIN_ID = os.getenv("DEFAULT_CHANNEL_ID", 220849530730577920)
|
|
DB_NAME = os.getenv("DB_NAME", "discord.db")
|
|
DB = DiscordDB(DB_NAME)
|
|
|
|
FLAGS = {
|
|
1: "flag_ro",
|
|
9: "flag_br",
|
|
10: "flag_it",
|
|
11: "flag_fr",
|
|
12: "flag_de",
|
|
13: "flag_hu",
|
|
14: "flag_cn",
|
|
15: "flag_es",
|
|
23: "flag_ca",
|
|
24: "flag_us",
|
|
26: "flag_mx",
|
|
27: "flag_ar",
|
|
28: "flag_ve",
|
|
29: "flag_gb",
|
|
30: "flag_ch",
|
|
31: "flag_nl",
|
|
32: "flag_be",
|
|
33: "flag_at",
|
|
34: "flag_cz",
|
|
35: "flag_pl",
|
|
36: "flag_sk",
|
|
37: "flag_no",
|
|
38: "flag_se",
|
|
39: "flag_fi",
|
|
40: "flag_ua",
|
|
41: "flag_ru",
|
|
42: "flag_bg",
|
|
43: "flag_tr",
|
|
44: "flag_gr",
|
|
45: "flag_jp",
|
|
47: "flag_kr",
|
|
48: "flag_in",
|
|
49: "flag_id",
|
|
50: "flag_au",
|
|
51: "flag_za",
|
|
52: "flag_md",
|
|
53: "flag_pt",
|
|
54: "flag_ie",
|
|
55: "flag_de",
|
|
56: "flag_ir",
|
|
57: "flag_pk",
|
|
58: "flag_il",
|
|
59: "flag_th",
|
|
61: "flag_si",
|
|
63: "flag_hr",
|
|
64: "flag_cl",
|
|
65: "flag_rs",
|
|
66: "flag_my",
|
|
67: "flag_ph",
|
|
68: "flag_sg",
|
|
69: "flag_ba",
|
|
70: "flag_ee",
|
|
71: "flag_lv",
|
|
72: "flag_lt",
|
|
73: "flag_kp",
|
|
74: "flag_uy",
|
|
75: "flag_py",
|
|
76: "flag_bo",
|
|
77: "flag_pe",
|
|
78: "flag_co",
|
|
79: "flag_mk",
|
|
80: "flag_me",
|
|
81: "flag_tw",
|
|
82: "flag_cy",
|
|
83: "flag_by",
|
|
84: "flag_nz",
|
|
164: "flag_sa",
|
|
165: "flag_eg",
|
|
166: "flag_ae",
|
|
167: "flag_al",
|
|
168: "flag_ge",
|
|
169: "flag_am",
|
|
170: "flag_ng",
|
|
171: "flag_cu",
|
|
}
|
|
|
|
MENTION_MAPPING = {1: "D1", 2: "D2", 3: "D3", 4: "D4", 11: "Air"}
|
|
|
|
__last_battle_response = None
|
|
__last_battle_update_timestamp = 0
|
|
|
|
|
|
def timestamp_to_datetime(timestamp: int) -> datetime.datetime:
|
|
return datetime.datetime.fromtimestamp(timestamp)
|
|
|
|
|
|
def s_to_human(seconds: Union[int, float]) -> str:
|
|
seconds = int(seconds)
|
|
h = seconds // 3600
|
|
m = (seconds - (h * 3600)) // 60
|
|
s = seconds % 60
|
|
return f"{h:01d}:{m:02d}:{s:02d}"
|
|
|
|
|
|
def get_battle_page():
|
|
global __last_battle_update_timestamp, __last_battle_response
|
|
if int(datetime.datetime.now().timestamp()) >= __last_battle_update_timestamp + 60:
|
|
dt = datetime.datetime.now()
|
|
r = requests.get("https://www.erepublik.com/en/military/campaignsJson/list")
|
|
try:
|
|
__last_battle_response = r.json()
|
|
except JSONDecodeError:
|
|
logger.warning("Received non json response from erep.lv/battles.json!")
|
|
return get_battle_page()
|
|
__last_battle_update_timestamp = __last_battle_response.get("last_updated", int(dt.timestamp()))
|
|
return __last_battle_response
|
|
|
|
|
|
class MyClient(discord.Client):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
# create the background task and run it in the background
|
|
self.bg_task = self.loop.create_task(self.report_epics())
|
|
|
|
@property
|
|
def timestamp(self):
|
|
return int(datetime.datetime.now().timestamp())
|
|
|
|
async def on_ready(self):
|
|
print("Client running")
|
|
print("------")
|
|
|
|
async def on_error(self, event_method, *args, **kwargs):
|
|
logger.warning(f"Ignoring exception in {event_method}")
|
|
|
|
async def report_epics(self):
|
|
await self.wait_until_ready()
|
|
roles = [role for role in self.get_guild(300297668553605131).roles if role.name in MENTION_MAPPING.values()]
|
|
role_mapping = {role.name: role.mention for role in roles}
|
|
while not self.is_closed():
|
|
try:
|
|
r = get_battle_page()
|
|
if not isinstance(r.get("battles"), dict):
|
|
sleep_seconds = r.get("last_updated") + 60 - self.timestamp
|
|
await asyncio.sleep(sleep_seconds if sleep_seconds > 0 else 0)
|
|
continue
|
|
for bid, battle in r.get("battles", {}).items():
|
|
for div in battle.get("div", {}).values():
|
|
if div.get("epic") > 1 and not DB.get_epic(div.get("id")):
|
|
with open(f"debug/{self.timestamp}.json", "w") as f:
|
|
json.dump(r, f)
|
|
invader_id = battle["inv"]["id"]
|
|
defender_id = battle["def"]["id"]
|
|
await self.get_channel(DEFAULT_CHANNEL_ID).send(
|
|
f"{role_mapping[MENTION_MAPPING[div['div']]]} Epic battle :{FLAGS[invader_id]}: vs :{FLAGS[defender_id]}:! "
|
|
f"Round time {s_to_human(self.timestamp - battle['start'])}\n"
|
|
f"https://www.erepublik.com/en/military/battlefield/{battle['id']}"
|
|
)
|
|
DB.add_epic(div.get("id"))
|
|
|
|
sleep_seconds = r.get("last_updated") + 60 - self.timestamp
|
|
await asyncio.sleep(sleep_seconds if sleep_seconds > 0 else 0)
|
|
except Exception as e:
|
|
logger.error("Discord bot's eRepublik epic watcher died!", exc_info=e)
|
|
try:
|
|
with open(f"debug/{self.timestamp}.json", "w") as f:
|
|
f.write(r.text)
|
|
except NameError:
|
|
logger.error("There was no Response object!", exc_info=e)
|
|
await asyncio.sleep(10)
|
|
await self.get_channel(DEFAULT_CHANNEL_ID).send(f"<@{ADMIN_ID}> I've stopped, please restart")
|
|
|
|
|
|
loop = asyncio.get_event_loop()
|
|
client = MyClient()
|
|
bot = commands.Bot(command_prefix="!")
|
|
|
|
|
|
@bot.event
|
|
async def on_ready():
|
|
print("Bot loaded")
|
|
# print(bot.user.name)
|
|
# print(bot.user.id)
|
|
print("------")
|
|
|
|
|
|
@bot.command()
|
|
async def kill(ctx):
|
|
if ctx.author.id == ADMIN_ID:
|
|
await ctx.send(f"{ctx.author.mention} Bye!")
|
|
sys.exit(1)
|
|
else:
|
|
await ctx.send(f"Labs mēģinājums! Mani nogalināt var tikai <@{ADMIN_ID}>")
|
|
|
|
|
|
def main():
|
|
global loop
|
|
loop.create_task(bot.start(DISCORD_TOKEN))
|
|
loop.create_task(client.start(DISCORD_TOKEN))
|
|
loop.run_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|