mirror of
https://github.com/house-of-vanity/doka2_info_bot.git
synced 2025-07-06 16:44:08 +00:00
Init
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
__pycache__/
|
5
Dockerfile
Normal file
5
Dockerfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
FROM python:3.7.4-stretch
|
||||||
|
WORKDIR /doka2_bot
|
||||||
|
COPY . /doka2_bot
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
CMD python3 /doka2_bot/app.py
|
241
app.py
Normal file
241
app.py
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# This program is dedicated to the public domain under the CC0 license.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from telegram import *
|
||||||
|
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
|
||||||
|
from database import DataBase
|
||||||
|
|
||||||
|
DB = DataBase('data.sql')
|
||||||
|
|
||||||
|
TOKEN = os.environ.get('TOKEN')
|
||||||
|
if TOKEN == None:
|
||||||
|
print('Define TOKEN env var!')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Enable logging
|
||||||
|
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
|
level=logging.INFO)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def dota(update, context):
|
||||||
|
if len(context.args) == 0:
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Patches", callback_data="patches"),
|
||||||
|
InlineKeyboardButton("Heroes", callback_data="heroes"),
|
||||||
|
InlineKeyboardButton("Items", callback_data="items"),
|
||||||
|
InlineKeyboardButton("Close", callback_data="close"),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
update.message.reply_text('What do you wanna know?', reply_markup=reply_markup)
|
||||||
|
|
||||||
|
def smart_append(line, text, lenght=4000):
|
||||||
|
if len(line) + len(text) > 4000:
|
||||||
|
text = text[len(line):]
|
||||||
|
text = 'Full message too long\n' + text
|
||||||
|
return text + line
|
||||||
|
|
||||||
|
def shorten(text):
|
||||||
|
max_len = 4000
|
||||||
|
if len(text) > max_len:
|
||||||
|
text = text[len(text)-max_len:]
|
||||||
|
return 'Message too long...\n' + text
|
||||||
|
|
||||||
|
def button(update, context):
|
||||||
|
query = update.callback_query
|
||||||
|
if query.data.split('_')[0] == 'close':
|
||||||
|
query.edit_message_text('Bye')
|
||||||
|
if query.data.split('_')[0] == 'dota':
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Patches", callback_data="patches"),
|
||||||
|
InlineKeyboardButton("Heroes", callback_data="heroes"),
|
||||||
|
InlineKeyboardButton("Items", callback_data="items"),
|
||||||
|
InlineKeyboardButton("Close", callback_data="close"),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
query.edit_message_text('What do you wanna know?', reply_markup=reply_markup)
|
||||||
|
if query.data.split('_')[0] == 'patch':
|
||||||
|
patch = query.data.split('_')[1]
|
||||||
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("Back", callback_data='patches'),]])
|
||||||
|
text = f'Patch *{patch}*\n'
|
||||||
|
general_info = DB.get_general_history(patch)
|
||||||
|
i = 1
|
||||||
|
for change in general_info:
|
||||||
|
text += f" {i} - {change[0]}\n"
|
||||||
|
i += 1
|
||||||
|
query.edit_message_text(text, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup)
|
||||||
|
if query.data.split('_')[0] == 'item':
|
||||||
|
keyboard = [[InlineKeyboardButton("Back", callback_data='items')]]
|
||||||
|
if 'expand' in query.data:
|
||||||
|
expand = True
|
||||||
|
else:
|
||||||
|
expand = False
|
||||||
|
keyboard[0].append(InlineKeyboardButton(
|
||||||
|
"Expand",
|
||||||
|
callback_data=f'{query.data}_expand')
|
||||||
|
)
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
expand = True if 'expand' in query.data else False
|
||||||
|
item_name = query.data.split('_')[1]
|
||||||
|
item_history = DB.get_item_history(item_name)
|
||||||
|
text = f'*{item_name}* update history\n'
|
||||||
|
cur_patch = ''
|
||||||
|
for upd in item_history:
|
||||||
|
if upd[1] is None and not expand:
|
||||||
|
continue
|
||||||
|
if upd[0] != cur_patch:
|
||||||
|
no_info = '*No changes*' if upd[1] is None else ''
|
||||||
|
text += f"\n* {upd[0]} {no_info} *\n"
|
||||||
|
cur_patch = upd[0]
|
||||||
|
if upd[1] is not None:
|
||||||
|
text += f'\t\t 🔹 {upd[1]}\n'
|
||||||
|
query.edit_message_text(text, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup)
|
||||||
|
|
||||||
|
if query.data.split('_')[0] == 'hero':
|
||||||
|
keyboard = [[InlineKeyboardButton("Back", callback_data='heroes')]]
|
||||||
|
if 'expand' in query.data:
|
||||||
|
expand = True
|
||||||
|
else:
|
||||||
|
expand = False
|
||||||
|
keyboard[0].append(InlineKeyboardButton(
|
||||||
|
"Expand",
|
||||||
|
callback_data=f'{query.data}_expand')
|
||||||
|
)
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
expand = True if 'expand' in query.data else False
|
||||||
|
hero_name = query.data.split('_')[1]
|
||||||
|
hero_history = DB.get_hero_history(hero_name)
|
||||||
|
text = f'*{hero_name}* update history\n'
|
||||||
|
cur_patch = ''
|
||||||
|
cur_type = ''
|
||||||
|
for upd in hero_history:
|
||||||
|
if upd[0] != cur_patch:
|
||||||
|
if upd[1] is None and not expand:
|
||||||
|
continue
|
||||||
|
no_info = '*No changes*' if upd[1] is None else ''
|
||||||
|
text += f"\n* {upd[0]} {no_info} *\n"
|
||||||
|
cur_patch = upd[0]
|
||||||
|
cur_type = ''
|
||||||
|
if upd[1] is not None:
|
||||||
|
if upd[1] != cur_type:
|
||||||
|
# text = smart_append(f"🔆*{upd[1].capitalize()}*\n", text)
|
||||||
|
text += f"🔆*{upd[1].capitalize()}*\n"
|
||||||
|
cur_type = upd[1]
|
||||||
|
# text = smart_append(f'\t\t 🔹 {upd[2]}\n', text)
|
||||||
|
text += f'\t\t 🔹 {upd[2]}\n'
|
||||||
|
text = shorten(text)
|
||||||
|
query.edit_message_text(text, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup)
|
||||||
|
|
||||||
|
if query.data.split('_')[0] == 'patches':
|
||||||
|
patches = DB.get_patch_list()
|
||||||
|
patches.reverse()
|
||||||
|
keyboard = [[]]
|
||||||
|
in_a_row = 5
|
||||||
|
for patch in patches:
|
||||||
|
if len(keyboard[-1]) == in_a_row:
|
||||||
|
keyboard.append(list())
|
||||||
|
keyboard[-1].append(InlineKeyboardButton(f"{patch}", callback_data=f"patch_{patch}"))
|
||||||
|
keyboard.append([InlineKeyboardButton("Back", callback_data='dota')])
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
query.edit_message_text(text="Select patch", reply_markup=reply_markup)
|
||||||
|
if query.data.split('_')[0] == 'heroes':
|
||||||
|
per_page = 20
|
||||||
|
try:
|
||||||
|
page = int(query.data.split('_')[1])
|
||||||
|
except:
|
||||||
|
page = 0
|
||||||
|
heroes = DB.get_heroes_list()
|
||||||
|
heroes.sort()
|
||||||
|
last_hero = page*per_page+per_page
|
||||||
|
if len(heroes) <= last_hero - 1:
|
||||||
|
last_hero = len(heroes)
|
||||||
|
keyboard = [[]]
|
||||||
|
in_a_row = 2
|
||||||
|
for hero in heroes[page*per_page:last_hero]:
|
||||||
|
if len(keyboard[-1]) == in_a_row:
|
||||||
|
keyboard.append(list())
|
||||||
|
keyboard[-1].append(InlineKeyboardButton(f"{hero}", callback_data=f"hero_{hero}"))
|
||||||
|
keyboard.append([])
|
||||||
|
if page != 0:
|
||||||
|
keyboard[-1].append(
|
||||||
|
InlineKeyboardButton("<=", callback_data=f'heroes_{page-1}'),
|
||||||
|
)
|
||||||
|
if len(heroes) != last_hero:
|
||||||
|
keyboard[-1].append(
|
||||||
|
InlineKeyboardButton("=>", callback_data=f'heroes_{page+1}'),
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.append([InlineKeyboardButton("Back", callback_data='dota')])
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
query.edit_message_text(text=f"Select hero {page*per_page}:{page*per_page+per_page}", reply_markup=reply_markup)
|
||||||
|
if query.data.split('_')[0] == 'items':
|
||||||
|
per_page = 20
|
||||||
|
try:
|
||||||
|
page = int(query.data.split('_')[1])
|
||||||
|
except:
|
||||||
|
page = 0
|
||||||
|
items = DB.get_items_list()
|
||||||
|
items.sort()
|
||||||
|
keyboard = [[]]
|
||||||
|
last_item = page*per_page+per_page
|
||||||
|
if len(items) <= last_item - 1:
|
||||||
|
last_item = len(items)
|
||||||
|
in_a_row = 2
|
||||||
|
for item in items[page*per_page:last_item]:
|
||||||
|
if len(keyboard[-1]) == in_a_row:
|
||||||
|
keyboard.append(list())
|
||||||
|
keyboard[-1].append(InlineKeyboardButton(f"{item}", callback_data=f"item_{item}"))
|
||||||
|
keyboard.append([])
|
||||||
|
if page != 0:
|
||||||
|
keyboard[-1].append(
|
||||||
|
InlineKeyboardButton("<=", callback_data=f'items_{page-1}'),
|
||||||
|
)
|
||||||
|
if len(items) != last_item:
|
||||||
|
keyboard[-1].append(
|
||||||
|
InlineKeyboardButton("=>", callback_data=f'items_{page+1}'),
|
||||||
|
)
|
||||||
|
keyboard.append([InlineKeyboardButton("Back", callback_data='dota')])
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
query.edit_message_text(text="Select item", reply_markup=reply_markup)
|
||||||
|
|
||||||
|
def error(update, context):
|
||||||
|
"""Log Errors caused by Updates."""
|
||||||
|
logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run bot."""
|
||||||
|
updater = Updater(TOKEN, use_context=True)
|
||||||
|
|
||||||
|
dp = updater.dispatcher
|
||||||
|
|
||||||
|
dp.add_handler(CallbackQueryHandler(button))
|
||||||
|
dp.add_handler(CommandHandler("dota", dota,
|
||||||
|
pass_args=True,
|
||||||
|
pass_job_queue=True,
|
||||||
|
pass_chat_data=True))
|
||||||
|
|
||||||
|
# log all errors
|
||||||
|
dp.add_error_handler(error)
|
||||||
|
|
||||||
|
# Start the Bot
|
||||||
|
updater.start_polling()
|
||||||
|
|
||||||
|
# Block until you press Ctrl-C or the process receives SIGINT, SIGTERM or
|
||||||
|
# SIGABRT. This should be used most of the time, since start_polling() is
|
||||||
|
# non-blocking and will stop the bot gracefully.
|
||||||
|
updater.idle()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
36
data.sql
Normal file
36
data.sql
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
BEGIN TRANSACTION;
|
||||||
|
CREATE TABLE IF NOT EXISTS "heroes" (
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
PRIMARY KEY("name")
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "patches" (
|
||||||
|
"version" TEXT NOT NULL,
|
||||||
|
"release_date" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY("version")
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "items" (
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
PRIMARY KEY("name")
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "hero_changes" (
|
||||||
|
"type" TEXT NOT NULL,
|
||||||
|
"patch" INT NOT NULL,
|
||||||
|
"hero" INT NOT NULL,
|
||||||
|
"info" TEXT NOT NULL,
|
||||||
|
"meta" TEXT,
|
||||||
|
PRIMARY KEY("hero", "patch", "info")
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "item_changes" (
|
||||||
|
"patch" INT NOT NULL,
|
||||||
|
"item" INT NOT NULL,
|
||||||
|
"info" TEXT NOT NULL,
|
||||||
|
"meta" TEXT,
|
||||||
|
PRIMARY KEY("item", "patch", "info")
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "general_changes" (
|
||||||
|
"patch" INT NOT NULL,
|
||||||
|
"info" TEXT NOT NULL,
|
||||||
|
"meta" TEXT,
|
||||||
|
PRIMARY KEY("patch", "info")
|
||||||
|
);
|
||||||
|
COMMIT;
|
BIN
data.sqlite
Normal file
BIN
data.sqlite
Normal file
Binary file not shown.
256
database.py
Normal file
256
database.py
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2019, UltraDesu <ultradesu@hexor.ru>
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the <organization> nor the
|
||||||
|
# names of its contributors may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY UltraDesu <ultradesu@hexor.ru> ''AS IS'' AND ANY
|
||||||
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL UltraDesu <ultradesu@hexor.ru> BE LIABLE FOR ANY
|
||||||
|
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#
|
||||||
|
"""
|
||||||
|
.. module:: models
|
||||||
|
:synopsis: Contains database action primitives.
|
||||||
|
.. moduleauthor:: AB <github.com/house-of-vanity>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# class DataBase create or use existent SQLite database file. It provides
|
||||||
|
# high-level methods for database.
|
||||||
|
class DataBase:
|
||||||
|
"""This class create or use existent SQLite database file. It provides
|
||||||
|
high-level methods for database."""
|
||||||
|
def __init__(self, scheme, basefile='data.sqlite'):
|
||||||
|
"""
|
||||||
|
Constructor creates new SQLite database if
|
||||||
|
it doesn't exist. Uses SQL code from file for DB init.
|
||||||
|
:param scheme: sql filename
|
||||||
|
:type scheme: string
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.scheme = ''
|
||||||
|
self.basefile = basefile
|
||||||
|
try:
|
||||||
|
conn = self.connect(basefile=basefile)
|
||||||
|
except:
|
||||||
|
log.debug('Could not connect to DataBase.')
|
||||||
|
return None
|
||||||
|
with open(scheme, 'r') as scheme_sql:
|
||||||
|
sql = scheme_sql.read()
|
||||||
|
self.scheme = sql
|
||||||
|
if conn is not None:
|
||||||
|
try:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.executescript(sql)
|
||||||
|
except Exception as e:
|
||||||
|
log.debug(f'Could not create scheme - {e}')
|
||||||
|
else:
|
||||||
|
log.debug("Error! cannot create the database connection.")
|
||||||
|
log.info('DB created.')
|
||||||
|
self.close(conn)
|
||||||
|
|
||||||
|
def connect(self, basefile):
|
||||||
|
"""
|
||||||
|
Create connect object for basefile
|
||||||
|
:param basefile: SQLite database filename
|
||||||
|
:type basefile: string
|
||||||
|
:return: sqlite3 connect object
|
||||||
|
"""
|
||||||
|
log.debug("Open connection to %s" % basefile)
|
||||||
|
return sqlite3.connect(basefile, check_same_thread=False)
|
||||||
|
|
||||||
|
def execute(self, sql):
|
||||||
|
"""
|
||||||
|
Execute SQL code. First of all connect to self.basefile. Close
|
||||||
|
connection after execution.
|
||||||
|
:param sql: SQL code
|
||||||
|
:type sql: string
|
||||||
|
:return: list of response. Empty list when no rows are available.
|
||||||
|
"""
|
||||||
|
conn = self.connect(basefile=self.basefile)
|
||||||
|
log.debug("Executing: %s" % sql)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(sql)
|
||||||
|
conn.commit()
|
||||||
|
result = cursor.fetchall()
|
||||||
|
self.close(conn)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def add_patch(self, patch):
|
||||||
|
sql = f"INSERT OR IGNORE INTO patches('version') VALUES ('{patch}')"
|
||||||
|
self.execute(sql)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_hero(self, hero):
|
||||||
|
hero = hero.replace("'", "''")
|
||||||
|
sql = f"INSERT OR IGNORE INTO heroes('name') VALUES ('{hero}')"
|
||||||
|
self.execute(sql)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_item(self, item):
|
||||||
|
item = item.replace("'", "''")
|
||||||
|
sql = f"INSERT OR IGNORE INTO items('name') VALUES ('{item}')"
|
||||||
|
self.execute(sql)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_general_changes(self,
|
||||||
|
patch,
|
||||||
|
info):
|
||||||
|
info = info.replace("'", "''")
|
||||||
|
patch_id = self.get_patch_id(patch)
|
||||||
|
sql = f"""INSERT OR IGNORE INTO
|
||||||
|
general_changes('patch', 'info')
|
||||||
|
VALUES (
|
||||||
|
'{patch_id}',
|
||||||
|
'{info}')"""
|
||||||
|
self.execute(sql)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_item_changes(self,
|
||||||
|
patch,
|
||||||
|
item,
|
||||||
|
info):
|
||||||
|
item = item.replace("'", "''")
|
||||||
|
info = info.replace("'", "''")
|
||||||
|
item_id = self.get_item_id(item)
|
||||||
|
patch_id = self.get_patch_id(patch)
|
||||||
|
sql = f"""INSERT OR IGNORE INTO
|
||||||
|
item_changes('patch', 'item', 'info')
|
||||||
|
VALUES (
|
||||||
|
'{patch_id}',
|
||||||
|
'{item_id}',
|
||||||
|
'{info}')"""
|
||||||
|
self.execute(sql)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_hero_changes(self,
|
||||||
|
change_type,
|
||||||
|
patch,
|
||||||
|
hero,
|
||||||
|
info,
|
||||||
|
meta=None):
|
||||||
|
hero = hero.replace("'", "''")
|
||||||
|
info = info.replace("'", "''")
|
||||||
|
if meta:
|
||||||
|
meta = meta.replace("'", "''")
|
||||||
|
hero_id = self.get_hero_id(hero)
|
||||||
|
patch_id = self.get_patch_id(patch)
|
||||||
|
sql = f"""INSERT OR IGNORE INTO
|
||||||
|
hero_changes('type', 'patch', 'hero', 'info', 'meta')
|
||||||
|
VALUES (
|
||||||
|
'{change_type}',
|
||||||
|
'{patch_id}',
|
||||||
|
'{hero_id}',
|
||||||
|
'{info}',
|
||||||
|
'{meta}')"""
|
||||||
|
self.execute(sql)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_patch_id(self, patch):
|
||||||
|
sql = f"SELECT rowid FROM patches WHERE version = '{patch}'"
|
||||||
|
ret = self.execute(sql)
|
||||||
|
return ret[0][0]
|
||||||
|
|
||||||
|
def get_item_id(self, item):
|
||||||
|
sql = f"SELECT rowid FROM items WHERE name = '{item}'"
|
||||||
|
ret = self.execute(sql)
|
||||||
|
return ret[0][0]
|
||||||
|
|
||||||
|
def get_hero_id(self, hero):
|
||||||
|
sql = f"SELECT rowid FROM heroes WHERE name = '{hero}'"
|
||||||
|
ret = self.execute(sql)
|
||||||
|
return ret[0][0]
|
||||||
|
|
||||||
|
def get_items_list(self):
|
||||||
|
sql = f"SELECT name FROM items"
|
||||||
|
ret = self.execute(sql)
|
||||||
|
items = list()
|
||||||
|
for item in ret:
|
||||||
|
items.append(item[0])
|
||||||
|
return items
|
||||||
|
|
||||||
|
def get_heroes_list(self):
|
||||||
|
sql = f"SELECT name FROM heroes"
|
||||||
|
ret = self.execute(sql)
|
||||||
|
heroes = list()
|
||||||
|
for hero in ret:
|
||||||
|
heroes.append(hero[0])
|
||||||
|
return heroes
|
||||||
|
|
||||||
|
def get_patch_list(self):
|
||||||
|
sql = f"SELECT version FROM patches"
|
||||||
|
ret = self.execute(sql)
|
||||||
|
patches = list()
|
||||||
|
for patch in ret:
|
||||||
|
patches.append(patch[0])
|
||||||
|
return patches
|
||||||
|
|
||||||
|
def get_general_history(self, patch):
|
||||||
|
patch = patch.replace("'", "''")
|
||||||
|
patch = self.get_patch_id(patch)
|
||||||
|
sql = f"""SELECT gc.info FROM general_changes gc
|
||||||
|
LEFT JOIN patches p on p.ROWID = gc.patch
|
||||||
|
WHERE gc.patch = '{patch}'"""
|
||||||
|
return self.execute(sql)
|
||||||
|
|
||||||
|
def get_hero_history(self, hero):
|
||||||
|
hero = hero.replace("'", "''")
|
||||||
|
hero = self.get_hero_id(hero)
|
||||||
|
sql = f"""SELECT p.version, a.type, a.info, a.meta FROM
|
||||||
|
patches p
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT p.version, hc.type, hc.info, hc.meta FROM `heroes` h
|
||||||
|
LEFT JOIN hero_changes hc ON hc.hero = h.ROWID
|
||||||
|
LEFT JOIN patches p ON hc.patch = p.ROWID
|
||||||
|
WHERE hc.hero = {hero}
|
||||||
|
) a ON p.version = a.version
|
||||||
|
ORDER BY p.rowid DESC;"""
|
||||||
|
return self.execute(sql)
|
||||||
|
|
||||||
|
def get_item_history(self, item):
|
||||||
|
item = item.replace("'", "''")
|
||||||
|
item = self.get_item_id(item)
|
||||||
|
sql = f"""SELECT p.version, a.info FROM
|
||||||
|
patches p
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT p.version, ic.info FROM item_changes ic
|
||||||
|
LEFT JOIN patches p ON p.rowid = ic.patch
|
||||||
|
WHERE ic.item = {item}
|
||||||
|
) a ON p.version = a.version
|
||||||
|
ORDER BY p.rowid DESC"""
|
||||||
|
return self.execute(sql)
|
||||||
|
|
||||||
|
def close(self, conn):
|
||||||
|
"""
|
||||||
|
Close connection object instance.
|
||||||
|
:param conn: sqlite3 connection object
|
||||||
|
:type conn: object
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
log.debug("Close connection to %s" % self.basefile)
|
||||||
|
conn.close()
|
||||||
|
|
91
dota_news.py
Normal file
91
dota_news.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import urllib.request
|
||||||
|
import bs4
|
||||||
|
from database import DataBase
|
||||||
|
|
||||||
|
URL = "https://www.dota2.com/patches/"
|
||||||
|
|
||||||
|
DB = DataBase('data.sql')
|
||||||
|
|
||||||
|
raw_content = urllib.request.urlopen(URL)
|
||||||
|
content = raw_content.read().decode('utf8')
|
||||||
|
|
||||||
|
raw_content.close()
|
||||||
|
|
||||||
|
schema = bs4.BeautifulSoup(content, 'html.parser')
|
||||||
|
|
||||||
|
raw_patches = schema.find(id="PatchSelector")
|
||||||
|
patches = list()
|
||||||
|
for patch in raw_patches:
|
||||||
|
if isinstance(patch, bs4.element.NavigableString):
|
||||||
|
continue
|
||||||
|
if patch.text[0].isdigit():
|
||||||
|
patches.append(patch.text)
|
||||||
|
DB.add_patch(patch.text)
|
||||||
|
print(f"Found patches: {patches}")
|
||||||
|
|
||||||
|
#patches = ['7.22']
|
||||||
|
|
||||||
|
for patch in patches:
|
||||||
|
raw_content = urllib.request.urlopen(URL + patch)
|
||||||
|
content = raw_content.read().decode('utf8')
|
||||||
|
raw_content.close()
|
||||||
|
schema = bs4.BeautifulSoup(content, 'html.parser')
|
||||||
|
|
||||||
|
# hero updates
|
||||||
|
for hero in schema.findAll('div', {"class": 'HeroNotes'}):
|
||||||
|
hero_name = hero.select('.HeroName')[0].get_text()
|
||||||
|
DB.add_hero(hero_name)
|
||||||
|
patch_notes = list()
|
||||||
|
common_notes = hero.find('ul', {"class": 'HeroNotesList'})
|
||||||
|
if common_notes is not None:
|
||||||
|
for note in common_notes.findChildren("li", recursive=False):
|
||||||
|
patch_notes.append(note.text)
|
||||||
|
DB.add_hero_changes(
|
||||||
|
change_type='common',
|
||||||
|
patch=patch,
|
||||||
|
hero=hero_name,
|
||||||
|
info=note.text)
|
||||||
|
ability_notes = dict()
|
||||||
|
for ability in hero.findAll('div', {"class": 'HeroAbilityNotes'}):
|
||||||
|
ability_name = ability.select('.AbilityName')[0].text
|
||||||
|
ability_notes[ability_name] = list()
|
||||||
|
for note in ability.findAll('li', {"class": 'PatchNote'}):
|
||||||
|
ability_notes[ability_name].append(note.text)
|
||||||
|
DB.add_hero_changes(
|
||||||
|
change_type='ability',
|
||||||
|
patch=patch,
|
||||||
|
hero=hero_name,
|
||||||
|
info=note.text,
|
||||||
|
meta=ability_name)
|
||||||
|
talent_notes = list()
|
||||||
|
talents = hero.find('div', {"class": 'TalentNotes'})
|
||||||
|
if talents is not None:
|
||||||
|
for talent in talents.findAll('li'):
|
||||||
|
talent_notes.append(talent.text)
|
||||||
|
DB.add_hero_changes(
|
||||||
|
change_type='talent',
|
||||||
|
patch=patch,
|
||||||
|
hero=hero_name,
|
||||||
|
info=talent.text)
|
||||||
|
|
||||||
|
# item updates
|
||||||
|
item_notes = dict()
|
||||||
|
for item in schema.findAll('div', {"class": 'ItemNotes'}):
|
||||||
|
item_name = item.select('.ItemName')[0].get_text()
|
||||||
|
DB.add_item(item_name)
|
||||||
|
item_notes[item_name] = list()
|
||||||
|
for note in item.findAll('li', {"class": 'PatchNote'}):
|
||||||
|
item_notes[item_name].append(note.text)
|
||||||
|
DB.add_item_changes(
|
||||||
|
patch=patch,
|
||||||
|
item=item_name,
|
||||||
|
info=note.text)
|
||||||
|
|
||||||
|
# general updates
|
||||||
|
general = schema.find(id='GeneralSection')
|
||||||
|
print(general)
|
||||||
|
if general is not None:
|
||||||
|
for note in general.findAll('li', {"class": 'PatchNote'}):
|
||||||
|
DB.add_general_changes(
|
||||||
|
patch=patch,
|
||||||
|
info=note.text)
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
beautifulsoup4==4.8.0
|
||||||
|
python-telegram-bot==12.0.0b1
|
Reference in New Issue
Block a user