25 Commits

Author SHA1 Message Date
77c59a91bd Dummy commit 2018-09-26 18:48:22 +03:00
287b5b346b Fix CI procedure 2018-09-26 18:34:46 +03:00
0dcb2d6f9b Add Ci procedure 2018-09-26 18:24:55 +03:00
4d463182aa Merge branch 'master' of https://github.com/hillsofeternity1/fesmoo_perdoliq 2018-06-04 08:16:11 +00:00
52e78ef94d Update cookies. 2018-06-04 08:15:44 +00:00
da9c098623 Try to catch telegram api network issues and prevent bot crash. 2018-05-24 16:17:20 +03:00
b0ff3b9034 Merge branch 'master' of github.com:hillsofeternity1/fesmoo_perdoliq 2018-05-17 20:08:23 +03:00
506978ea76 Database features. Development has started. 2018-05-17 20:07:50 +03:00
21527657b2 Update README.md 2018-05-17 19:40:02 +03:00
7341eaaa4e Multithreading message handling. 2018-05-17 16:33:53 +00:00
6b948d4b2b Fix include issue 2018-05-17 15:58:53 +00:00
67244888af Fix 2018-05-17 18:43:54 +03:00
358c6b348b Added dryrun mode. Also tg bot key has been moved to tg_settings file 2018-05-17 18:42:45 +03:00
AB
03748de34e Update command style 2018-05-12 15:05:59 +03:00
mk
2f3e43f937 added beautiful picture 2018-05-12 00:36:56 +02:00
mk
6b711e7151 added beautiful picture 2018-05-12 00:36:34 +02:00
fcb5b74e41 Fix message parse error. 2018-05-11 22:11:07 +00:00
484636369d Make telegram interface more user friendly. 2018-05-11 21:30:42 +00:00
408c1e890b Make telegram interface more user friendly. 2018-05-11 21:29:39 +00:00
AB
69ff9de93d PIZDA 2018-05-11 23:09:11 +03:00
AB
da97ee7c99 PIZDA 2018-05-11 23:08:29 +03:00
mk
6c882a8e02 fixes 2018-05-11 22:03:35 +02:00
mk
9a2d216691 fixes 2018-05-11 21:50:45 +02:00
mk
f135c613a9 removet useless action 2018-05-11 21:27:43 +02:00
mk
2de7d4bd56 added baraban 2018-05-11 20:46:37 +02:00
12 changed files with 499 additions and 56 deletions

3
.gitignore vendored
View File

@ -1,5 +1,8 @@
# configs
creds.ini
tg_settings.py
*.swp
*.swo
# Byte-compiled / optimized / DLL files
__pycache__/

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM ubuntu:18.04
RUN apt-get update && \
apt-get install -y python3 python3-pip && \
pip3 install python-telegram-bot && \
pip3 install requests && \
pip3 install beautifulsoup4
WORKDIR /usr/share/fesmoo_perdoliq
COPY ./*py /usr/share/fesmoo_perdoliq/
CMD python3 baraban.py

View File

@ -1,2 +1,14 @@
# fesmoo_perdoliq
dirty hack for kids
It is a Telegram bot which is using OpenPerdoliqlib for resolving tests on fesmu university website.
here is docker image https://hub.docker.com/r/ultradesu/fesmoo_perdoliq/
# python3 only
reqs:
pip install requests
pip install beautifulsoup4
pip install python-telegram-bot

152
baraban.py Executable file
View File

@ -0,0 +1,152 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Simple Bot to reply to Telegram messages.
This is built on the API wrapper, see echobot2.py to see the same example built
on the telegram.ext bot framework.
This program is dedicated to the public domain under the CC0 license.
"""
import os
import sys
import logging
import threading
import telegram
from telegram.error import NetworkError, Unauthorized
from time import sleep
try:
TOKEN = os.environ["TG_TOKEN"]
except:
print("You have to set env var TG_TOKEN with bot token.")
sys.exit(1)
try:
if os.environ["DRYRUN"]:
from dryrun import Perdoliq
except:
from main import Perdoliq
update_id = None
def main():
"""Run the bot."""
global update_id
# Telegram Bot Authorization Token
bot = telegram.Bot(TOKEN)
# get the first pending update_id,
# this is so we can skip over it in case
# we get an "Unauthorized" exception.
try:
update_id = bot.get_updates()[0].update_id
except IndexError:
update_id = None
logging.basicConfig(level=logging.DEBUG)
while True:
try:
handle_update(bot)
except NetworkError:
sleep(1)
except Unauthorized:
# The user has removed or blocked the bot.
update_id += 1
def perdoliq(username, password, subj, test, acc, is_delayed):
try:
app = Perdoliq(username, password)
app.auth()
app.resolve(subj, test, acc, is_delayed=int(is_delayed))
except Exception as e:
return "Exception: " + str(e)
def list_test(username, password):
try:
app = Perdoliq(username, password)
app.auth()
return (app.get_tests())
except Exception as e:
return "Exception: " + str(e)
def handle_update(bot):
"""Echo the message the user sent."""
global update_id
# Request updates after the last update_id
for update in bot.get_updates(offset=update_id, timeout=10):
update_id = update.update_id + 1
if update.message:
# if there is and update, process it in threads
t = threading.Thread(target=do_action, args=(update,))
t.start()
def do_action(update):
try:
s = update.message.text.split()
except:
return 1
if s[0] == '/resolve':
if len(s) != 7:
update.message.reply_markdown("Missing operand... Try again")
update.message.reply_markdown("Usage: */resolve <user[text]> "\
"<pass[text]> <subj[int]> <test[int]> "\
"<accuracy[0-100]> <commit[1/0]>*")
return False
msg = "Please wait. If you have chosen commit=1, so test "\
"going to be resolved in about 20 minutes and will "\
"be commited automatically, otherwise it will take "\
"about a 2 minutes and you have to "\
"commit it by yourself. Just wait. PS you have "\
"chosen subj %s "\
"test %s and accuracy %s" % (s[3], s[4], s[5])
update.message.reply_text(msg)
update.message.reply_text("You cannot resolve more than "\
"one test in the same time."\
"Одновременно решать более одного теста невозможно,"\
" вы испортите результаты обоих тестов.")
perdoliq(s[1], s[2], s[3], s[4], s[5], s[6])
update.message.reply_text("It's done. Check your test because "\
"i disclaim any responsibility.")
elif s[0] == '/list':
try:
if len(s) == 3:
update.message.reply_text("Fetching tests...")
sleep(5)
tests = list_test(s[1], s[2])
#print("******",tests)
else:
update.message.reply_markdown("Usage: */list <user[text]>"\
" <pass[text]>*")
return False
except:
update.message.reply_markdown("Usage: */list <user[text]>"\
" <pass[text]>*")
return False
msg = "Here is an available tests:\n``` "
i = 0
#print("******",tests)
for subj in tests:
msg = msg + (" [%s] %s\n" % (i, subj))
i += 1
j = 0
for test in tests[subj]:
msg = msg + (" [%s] %s\n" % (j, test))
j += 1
update.message.reply_markdown(msg + "```\n Pay attention to "\
"numbers in brackets \[..] *Here is subj and test numbers*")
else:
update.message.reply_markdown("Possible commands: */resolve, /list*")
update.message.reply_markdown("Usage: */resolve <user[text]> "\
"<pass[text]> <subj[int]> <test[int]> "\
"<accuracy[0-100]> <commit[1/0]>*")
update.message.reply_markdown("Usage: */list <user[text]> "\
"<pass[text]>*")
if __name__ == '__main__':
try:
main()
except:
pass

94
bot.py Executable file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Simple Bot to reply to Telegram messages.
This is built on the API wrapper, see echobot2.py to see the same example built
on the telegram.ext bot framework.
This program is dedicated to the public domain under the CC0 license.
"""
import os
import logging
import threading
import telegram
from telegram.error import NetworkError, Unauthorized
from time import sleep
try:
import tg_settings
except:
print("You have to create tg_settings.py. See tg_settings.py.example.")
os.exit(1)
try:
if os.environ["DRYRUN"]:
from dryrun import Perdoliq
except:
from main import Perdoliq
update_id = None
def main():
"""Run the bot."""
global update_id
# Telegram Bot Authorization Token
TOKEN = tg_settings.TG_TOKEN
bot = telegram.Bot(TOKEN)
# get the first pending update_id,
# this is so we can skip over it in case
# we get an "Unauthorized" exception.
try:
update_id = bot.get_updates()[0].update_id
except IndexError:
update_id = None
logging.basicConfig(level=logging.DEBUG)
while True:
try:
handle_update(bot)
except NetworkError:
sleep(1)
except Unauthorized:
# The user has removed or blocked the bot.
update_id += 1
def perdoliq(username, password, subj, test, acc, is_delayed):
try:
app = Perdoliq(username, password)
app.auth()
app.resolve(subj, test, acc, is_delayed=int(is_delayed))
except Exception as e:
return "Exception: " + str(e)
def list_test(username, password):
try:
app = Perdoliq(username, password)
app.auth()
return (app.get_tests())
except Exception as e:
return "Exception: " + str(e)
def handle_update(bot):
"""Echo the message the user sent."""
global update_id
# Request updates after the last update_id
for update in bot.get_updates(offset=update_id, timeout=10):
update_id = update.update_id + 1
if update.message:
# if there is and update, process it in threads
t = threading.Thread(target=do_action, args=(update,))
t.start()
def do_action(update):
try:
s = update.message.text.split()
except:
return 1
# if s[0] == '/resolve':
# return 0
if __name__ == '__main__':
main()

41
database.py Normal file
View File

@ -0,0 +1,41 @@
import datetime as dt
class DataBase:
def __init__(self, basefile, scheme):
import sqlite3
self.scheme = ''
try:
self.conn = sqlite3.connect(basefile, check_same_thread=False)
except:
print('Could not connect to DataBase.')
return None
with open(scheme, 'r') as scheme_sql:
sql = scheme_sql.read()
self.scheme = sql
if self.conn is not None:
try:
cursor = self.conn.cursor()
cursor.executescript(sql)
except:
print('Could not create scheme.')
else:
print("Error! cannot create the database connection.")
print('DB created.')
def execute(self, sql):
cursor = self.conn.cursor()
cursor.execute(sql)
self.conn.commit()
return cursor.fetchall()
def update_user(self, user_name, user_id):
date = int(dt.datetime.now().strftime("%s"))
sql = """INSERT OR IGNORE INTO
users('user_id', 'user_name', 'date')
VALUES ('%s','%s','%s')""" % (
user_id,
user_name,
date
)
self.execute(sql)

132
dryrun.py Normal file
View File

@ -0,0 +1,132 @@
import requests
import settings
import logging
import re
from bs4 import BeautifulSoup
from random import randint
import time
logging.basicConfig(level=logging.INFO)
class Perdoliq:
def __init__(self, username, password):
self.password = password
self.username = username
self.SessionId = ''
self.name = ''
self.subjects = {}
# make auth
def auth(self):
self.SessionId = '** DRY_RUN_ASP.NET_SessionId **'
return self.SessionId
# update and return list of subjs and tests
def get_tests(self):
self.subjects = {
'Аналитическая химия': [
'броматометрическое титрование',
'Качественный анализ. Теоретические основы. Итоговый',
'Химический количественный анализ. Итоговый', 'Йодометрия',
'редоксиметрия. нитритометрия',
'Количественный анализ. Протолитометрия.'
],
'Безопасность жизнедеятельности, медицина катастроф': [
'Мобилизационная подготовка здравоохранения',
'Медицина катастроф', 'Гражданская оборона'
],
'Биологическая химия': [
'Химия белков', 'Ферменты', 'Витамины',
'Нуклеиновые кислоты и матричные биосинтезы',
'Обмен углеводов', 'Энергетический обмен'
],
'Дисциплины по выбору Ф-04-1':
['физико-химические методы анализа'],
'Иностранный язык': [
'лексико-грамматический 9', 'Лексико-грамматический 8',
'лексико-грамматический 10', 'лексико-грамматический 3а'
],
'Органическая химия': [
'Аминокислоты', 'Углеводы',
'Карбоновые кислоты, гетерофункуциональные соединения',
'Омыляемые липиды', 'Неомыляемые липиды',
'Гетероциклические соединения'
],
'Патология': [
'Аллергия', 'Патология системы иммунобиологического надзора',
'Нозология', 'Патология белкового обмена',
'Патология обмена жиров', 'Патология углеводного обмена',
'Патология водного и ионного обменов. Нарушение КОС.',
'ЛИХОРАДКА', 'Кислородное голодание',
'Патофизиология периферического кровобращения. Воспаление.'
],
'Первая доврачебная помощь': ['Первая доврачебная помощь'],
'Физическая культура': [
'Методы контроля за функциональным и физическим состоянием организма человека'
],
'Философия': [
'Онтология. Гносеология', 'Западная философия 19-20вв-2',
'Западная философия 19-20вв - 3', 'Онтология',
'Научное познание - 2', 'Западная философия 19-20вв',
'Философия истории', 'Философия человека - 1',
'Философия человека - 2', 'Бытие и сознание',
'Социальная философия - 1', 'Научное познание',
'Философия человека', 'Онтология - 2',
'Философия общества и глобальные проблемы человечества',
'Русская философия'
]
}
return self.subjects
def start_test(self, pred, test):
return 30
# predict answers for any question
def predict(self, q_number):
return ['2']
# mark answers into requested question
def answer(self, q_number, answers):
return 0
# commit test
def finish_test(self):
return 0
def resolve(self, subj, test, accuracy, is_delayed=False):
# renew auth
self.auth()
# get count of questions
q_count = self.start_test(subj, test)
def sleep_rand():
delay = randint(20, 40)
logging.info("Going to wait %s sec for this question." % delay)
time.sleep(delay)
accuracy = int(accuracy)
# applying accuracy here. trying to make resulting
# accuracy NO LESSthan requested.
spoil_count = int(q_count - q_count * (accuracy / 100))
if int(spoil_count / q_count * (-100) + 100) < accuracy:
spoil_count -= 1
# skip random `spoil_count` questions.
# Choose questions which going to be skipped
skip_this = []
for i in range(0, spoil_count):
skip_this.append(randint(1, q_count + 1))
# start resolve test
for i in range(1, q_count + 1):
if is_delayed == True:
sleep_rand()
if i not in skip_this:
prediction = self.predict(i)
self.answer(i, prediction)
# make autocommit
if is_delayed == True:
self.finish_test()

View File

@ -39,6 +39,7 @@ class Perdoliq:
settings.fesmu_root_url + 'startstu.aspx',
cookies={'ASP.NET_SessionId': self.SessionId})
soup = BeautifulSoup(r.text, "html.parser")
#print("****** ", soup)
_p = re.compile(',.*$')
self.name = _p.sub(
'', soup.find(id="ctl00_MainContent_Label1").get_text())[14:]
@ -143,7 +144,7 @@ class Perdoliq:
"There isn't any correct answers for %s. Looks like"\
" something went wrong. Trying other way...", q_number
)
# send all checkboxes as answer to 30th page
# send all checkboxes as answer to 30th page (4 answers questions)
requests.post(
settings.fesmu_root_url + 'studtst2.aspx',
data=settings.merge(
@ -232,12 +233,12 @@ class Perdoliq:
logging.info("Going to wait %s sec for this question." % delay)
time.sleep(delay)
accuracy = int(accuracy)
# applying accuracy here. trying to make resulting
# accuracy NO LESSthan requested.
spoil_count = int(q_count - q_count * (accuracy / 100))
if int(spoil_count / q_count * (-100) + 100) < accuracy:
spoil_count -= 1
# skip random `spoil_count` questions.
# Choose questions which going to be skipped
skip_this = []

View File

@ -1,5 +1,7 @@
python3
# python3 only
pip install requests
py -m pip install requests
pip install beautifulsoup4
pip install python-telegram-bot
py -m pip install beautifulsoup4

12
scheme.sql Normal file
View File

@ -0,0 +1,12 @@
BEGIN TRANSACTION;
-- DROP TABLE IF EXISTS `user`;
CREATE TABLE IF NOT EXISTS `user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`user_id` TEXT NOT NULL,
`user_name` TEXT NOT NULL,
`site_name` TEXT,
`site_user` TEXT,
`site_pass` TEXT,
`date` INTEGER NOT NULL
);
COMMIT;

File diff suppressed because one or more lines are too long

1
tg_settings.py.example Normal file
View File

@ -0,0 +1 @@
TG_TOKEN = '4712707:329GqTj222i8unS23367sXqeMsyE'