Updated to discord bot and its docker
gitignore dockerignore Formatting Requirement update, code styling Gitignore
This commit is contained in:
parent
00c3959273
commit
71e69719d7
13
.dockerignore
Normal file
13
.dockerignore
Normal file
@ -0,0 +1,13 @@
|
||||
__pycache__
|
||||
# Docker
|
||||
docker-compose.yml
|
||||
.docker
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*/__pycache__/
|
||||
**/__pycache__/
|
||||
*.py[cod]
|
||||
**/*.py[cod]
|
||||
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
||||
venv
|
||||
*.db
|
||||
*.log
|
||||
.env
|
||||
pid
|
||||
*pid
|
||||
__pycache__
|
||||
.idea
|
||||
debug/
|
||||
|
@ -1,7 +1,13 @@
|
||||
FROM python:3.9-slim
|
||||
WORKDIR /app
|
||||
RUN groupadd -g 1000 discordbot \
|
||||
&& useradd -u 1000 -g 1000 discordbot \
|
||||
&& mkdir /home/discordbot \
|
||||
&& chown -R discordbot:discordbot /app \
|
||||
&& chown -R discordbot:discordbot /home/discordbot
|
||||
|
||||
USER discordbot
|
||||
COPY requirements.txt /app/requirements.txt
|
||||
RUN pip install -r requirements.txt
|
||||
COPY . /app
|
||||
|
||||
CMD python discord_bot.py
|
||||
|
4
db.py
4
db.py
@ -1,4 +1,4 @@
|
||||
from typing import Union, Dict, Optional
|
||||
from typing import Dict, Optional, Union
|
||||
|
||||
from sqlite_utils import Database
|
||||
from sqlite_utils.db import NotFoundError
|
||||
@ -21,7 +21,7 @@ class DiscordDB:
|
||||
self._db.create_table("player", {"id": int, "name": str}, pk="id", not_null={"id", "name"})
|
||||
|
||||
if "epic" not in self._db.table_names():
|
||||
self._db.create_table("epic", {"id": int, }, pk="id", not_null={"id"})
|
||||
self._db.create_table("epic", {"id": int, "fake": bool}, pk="id", not_null={"id"}, defaults={"fake": False})
|
||||
|
||||
self._db.vacuum()
|
||||
|
||||
|
137
discord_bot.py
137
discord_bot.py
@ -18,8 +18,7 @@ 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')
|
||||
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
|
||||
@ -28,29 +27,94 @@ fh.setLevel(logging.DEBUG)
|
||||
logger.addHandler(fh)
|
||||
keep_fds = [fh.stream.fileno()]
|
||||
|
||||
os.makedirs('debug', exist_ok=True)
|
||||
os.makedirs("debug", exist_ok=True)
|
||||
|
||||
pidfile = "pid"
|
||||
with open(pidfile, 'w') as f:
|
||||
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_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'}
|
||||
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"}
|
||||
|
||||
@ -67,20 +131,20 @@ def s_to_human(seconds: Union[int, float]) -> str:
|
||||
h = seconds // 3600
|
||||
m = (seconds - (h * 3600)) // 60
|
||||
s = seconds % 60
|
||||
return f'{h:01d}:{m:02d}:{s:02d}'
|
||||
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://erep.lv/battles.json')
|
||||
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()))
|
||||
__last_battle_update_timestamp = __last_battle_response.get("last_updated", int(dt.timestamp()))
|
||||
return __last_battle_response
|
||||
|
||||
|
||||
@ -95,11 +159,11 @@ class MyClient(discord.Client):
|
||||
return int(datetime.datetime.now().timestamp())
|
||||
|
||||
async def on_ready(self):
|
||||
print('Client running')
|
||||
print('------')
|
||||
print("Client running")
|
||||
print("------")
|
||||
|
||||
async def on_error(self, event_method, *args, **kwargs):
|
||||
logger.warning(f'Ignoring exception in {event_method}')
|
||||
logger.warning(f"Ignoring exception in {event_method}")
|
||||
|
||||
async def report_epics(self):
|
||||
await self.wait_until_ready()
|
||||
@ -108,28 +172,27 @@ class MyClient(discord.Client):
|
||||
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
|
||||
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') and not DB.get_epic(div.get('id')):
|
||||
with open(f'debug/{self.timestamp}.json', 'wb') as f:
|
||||
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)
|
||||
await self.get_channel(DEFAULT_CHANNEL_ID).send(
|
||||
f"{role_mapping[MENTION_MAPPING[div['div']]]} Epic battle! 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'))
|
||||
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
|
||||
sleep_seconds = r.get("last_updated") + 60 - self.timestamp
|
||||
await asyncio.sleep(sleep_seconds if sleep_seconds > 0 else 0)
|
||||
except Exception as e:
|
||||
await self.get_channel(DEFAULT_CHANNEL_ID).send(
|
||||
f"<@{ADMIN_ID}> Something bad has happened with epic notifier!")
|
||||
logger.error("Discord bot's eRepublik epic watcher died!", exc_info=e)
|
||||
try:
|
||||
with open(f"{self.timestamp}.json", 'w') as f:
|
||||
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)
|
||||
@ -139,15 +202,15 @@ class MyClient(discord.Client):
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
client = MyClient()
|
||||
bot = commands.Bot(command_prefix='!')
|
||||
bot = commands.Bot(command_prefix="!")
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
print('Bot loaded')
|
||||
print("Bot loaded")
|
||||
# print(bot.user.name)
|
||||
# print(bot.user.id)
|
||||
print('------')
|
||||
print("------")
|
||||
|
||||
|
||||
@bot.command()
|
||||
|
6
docker_run.sh
Executable file
6
docker_run.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
docker rm -f discord_bot
|
||||
set -e
|
||||
docker build --tag discord_epicbot .
|
||||
docker run --detach -v $PWD:/app --restart=always --name discord_bot discord_epicbot
|
||||
|
4
pyproject.toml
Normal file
4
pyproject.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[tool.black]
|
||||
line-length = 140
|
||||
target-version = ['py38', 'py39']
|
||||
|
@ -1,4 +1,4 @@
|
||||
discord.py==1.7.3
|
||||
requests==2.26.0
|
||||
python-dotenv==0.18.0
|
||||
sqlite_utils==3.12
|
||||
python-dotenv==0.19.0
|
||||
sqlite_utils==3.13
|
||||
|
1
run.sh
1
run.sh
@ -1,5 +1,4 @@
|
||||
#!/bin/bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
echo "Checking queries..."
|
||||
python -m unittest
|
||||
|
18
tests.py
18
tests.py
@ -10,28 +10,28 @@ class TestDatabase(unittest.TestCase):
|
||||
self.db = DiscordDB()
|
||||
|
||||
def test_member(self):
|
||||
member = {'id': 1200, 'name': 'username'}
|
||||
member = {"id": 1200, "name": "username"}
|
||||
self.db.add_member(**member)
|
||||
self.assertEqual(self.db.add_member(**member), member)
|
||||
|
||||
self.assertRaises(NotFoundError, self.db.get_member, member_id=100)
|
||||
self.assertEqual(self.db.get_member(member_id=member['id']), member)
|
||||
self.assertEqual(self.db.get_member(member_id=member["id"]), member)
|
||||
|
||||
member.update(name="Success")
|
||||
self.assertTrue(self.db.update_member(member['id'], member['name']))
|
||||
self.assertEqual(self.db.get_member(member_id=member['id']), member)
|
||||
self.assertTrue(self.db.update_member(member["id"], member["name"]))
|
||||
self.assertEqual(self.db.get_member(member_id=member["id"]), member)
|
||||
|
||||
def test_player(self):
|
||||
player = {'id': 1, 'name': 'plato'}
|
||||
self.assertTrue(self.db.add_player(player['id'], player['name']))
|
||||
self.assertFalse(self.db.add_player(player['id'], player['name']))
|
||||
player = {"id": 1, "name": "plato"}
|
||||
self.assertTrue(self.db.add_player(player["id"], player["name"]))
|
||||
self.assertFalse(self.db.add_player(player["id"], player["name"]))
|
||||
self.assertEqual(self.db.get_player(0), None)
|
||||
self.assertEqual(self.db.get_player(player['id']), player)
|
||||
self.assertEqual(self.db.get_player(player["id"]), player)
|
||||
|
||||
self.assertFalse(self.db.update_player(0, "Error"))
|
||||
player["name"] = "New name"
|
||||
self.assertTrue(self.db.update_player(player["id"], player["name"]))
|
||||
self.assertEqual(self.db.get_player(player['id']), player)
|
||||
self.assertEqual(self.db.get_player(player["id"]), player)
|
||||
|
||||
def test_epic(self):
|
||||
self.assertFalse(self.db.get_epic(123456))
|
||||
|
Loading…
x
Reference in New Issue
Block a user