示例#1
0
def prepare_balance_mobilebalance(filter='FULL', params={}):
    """Формируем текст для отправки в telegram из html файла полученного из web сервера mobilebalance
    """
    url = store.options('mobilebalance_http',
                        section='Telegram',
                        mainparams=params)
    tgmb_format = store.options('tgmb_format',
                                section='Telegram',
                                mainparams=params)
    response1_text = requests.get(url).content.decode('cp1251')
    # нет таблицы
    if 'Введите пароль' in response1_text or '<table' not in response1_text:
        res = 'Неправильный пароль для страницы баланса в ini, проверьте параметр mobilebalance_http'
        return res
    soup = bs4.BeautifulSoup(response1_text, 'html.parser')
    headers = [
        ''.join(el.get('id')[1:])
        for el in soup.find(id='header').findAll('th')
    ]
    if filter == 'LASTCHANGE' and 'BalDeltaQuery' not in headers:  # нет колонки Delta (запрос)
        res = 'Включите показ колонки Delta (запрос) в настройках mobilebalance'
        return res
    elif filter == 'LASTDAYCHANGE' and 'BalDelta' not in headers:  # нет колонки Delta (день)
        res = 'Включите показ колонки Delta (день) в настройках mobilebalance'
        return res
    data = [[''.join(el.contents) for el in line.findAll(['th', 'td'])]
            for line in soup.findAll(id='row')]
    table = [dict(zip(headers, line)) for line in data]
    table = filter_balance(table, filter, params)
    res = [tgmb_format.format(**line) for line in table]
    return '\n'.join(res)
示例#2
0
def send_telegram_over_requests(text=None,
                                auth_id=None,
                                filter='FULL',
                                params={}):
    """Отправка сообщения в телеграм через requests без задействия python-telegram-bot
    Может пригодится при каких-то проблемах с ботом или в ситуации когда на одной машине у нас крутится бот, 
    а с другой в этого бота мы еще хотим засылать инфу
    text - сообщение, если не указано, то это баланс для телефонов у которых он изменился
    auth_id - список id через запятую на которые слать, если не указано, то берется список из mbplugin.ini 
    """
    if text is None:
        text = prepare_balance(filter, params)
    api_token = store.options('api_token',
                              section='Telegram',
                              mainparams=params).strip()
    if len(api_token) == 0:
        logging.info('Telegtam api_token not found')
        return
    if auth_id is None:
        auth_id = list(
            map(
                int,
                store.options('auth_id', section='Telegram',
                              mainparams=params).strip().split(',')))
    else:
        auth_id = list(map(int, str(auth_id).strip().split(',')))
    r = [
        requests.post(f'https://api.telegram.org/bot{api_token}/sendMessage',
                      data={
                          'chat_id': chat_id,
                          'text': text,
                          'parse_mode': 'HTML'
                      }) for chat_id in auth_id if text != ''
    ]
    return [repr(i) for i in r]
示例#3
0
 def main(self, run='normal'):
     self.browser_launch()
     if run == 'normal':
         self.data_collector()
     elif run == 'check_logon':
         self.check_logon_selectors_prepare()
         self.check_logon_selectors()
     logging.debug(f'Data ready {self.result.keys()}')
     if str(store.options('log_responses')) == '1' or store.options(
             'logginglevel') == 'DEBUG':
         import pprint
         text = '\n\n'.join([
             f'{k}\n{v if k.startswith("CONTENT") else pprint.PrettyPrinter(indent=4).pformat(v) }'
             for k, v in self.responses.items()
             if 'GetAdElementsLS' not in k and 'mc.yandex.ru' not in k
         ])
         with open(os.path.join(store.options('loggingfolder'),
                                self.storename + '.log'),
                   'w',
                   encoding='utf8',
                   errors='ignore') as f:
             f.write(text)
     self.browser_close()
     kill_chrome(
     )  # Добиваем  все наши незакрытые хромы, чтобы не появлялось кучи зависших
     clear_cache(self.storename)
     return self.result
示例#4
0
def flags(cmd, key=None, value=None):
    'Работаем с флагами (таблица Flags) если в ini установлен sqlitestore=1, если нет просто вернем None'
    try:
        if store.options('sqlitestore') == '1':
            dbfilename = store.options('dbfilename')
            logging.debug(f'Flag:{cmd}')
            db = dbengine(dbfilename)
            if cmd.lower() == 'set':
                db.cur.execute('REPLACE INTO flags(key,value) VALUES(?,?)',
                               [key, value])
                db.conn.commit()
            elif cmd.lower() == 'get':
                db.cur.execute('select value from flags where key=?', [key])
                qres = db.cur.fetchall()
                if len(qres) > 0:
                    return qres[0][0]
            elif cmd.lower() == 'getall':
                db.cur.execute('select * from flags')
                qres = db.cur.fetchall()
                return {k: v for k, v in qres}
            elif cmd.lower() == 'deleteall':
                db.cur.execute('delete from flags')
                db.conn.commit()
            elif cmd.lower() == 'delete':
                db.cur.execute('delete from flags where key=?', [key])
                db.conn.commit()
        else:
            if cmd.lower() == 'getall':
                return {}
    except Exception:
        logging.error(
            f'Ошибка при записи в БД {"".join(traceback.format_exception(*sys.exc_info()))}'
        )
示例#5
0
def write_report():
    'сохраняем отчет balance_html если в ini createhtmlreport=1'
    try:
        if store.options('createhtmlreport') == '1':
            _, res = getreport()
            balance_html = store.options('balance_html')
            logging.info(f'Создаем {balance_html}')
            open(balance_html, encoding='utf8', mode='w').write('\n'.join(res))
    except Exception:
        logging.error(
            f'Ошибка генерации {balance_html} {"".join(traceback.format_exception(*sys.exc_info()))}'
        )
示例#6
0
def write_result_to_db(plugin, login, result):
    'пишем в базу если в ini установлен sqlitestore=1'
    try:
        if store.options('sqlitestore') == '1':
            dbfilename = store.options('dbfilename')
            logging.info(f'Пишем в базу {dbfilename}')
            db = dbengine(dbfilename)
            db.write_result(plugin, login, result)
    except AttributeError:
        logging.info(f'Отсутствуют параметры {"".join(traceback.format_exception(*sys.exc_info()))} дополнительные действия не производятся')
    except Exception:
        logging.error(f'Ошибка при записи в БД {"".join(traceback.format_exception(*sys.exc_info()))}')
示例#7
0
def responses():
    'Возвращаем все responses словарем'
    try:
        if store.options('sqlitestore') == '1':
            dbfilename = store.options('dbfilename')
            logging.debug(f'Responses from sqlite')
            db = dbengine(dbfilename)
            db.cur.execute('select key,value from responses')
            qres = db.cur.fetchall()
            return {k: v for k, v in qres}
        else:
            return {}
    except Exception:
        logging.error(
            f'Ошибка при записи в БД {"".join(traceback.format_exception(*sys.exc_info()))}'
        )
示例#8
0
def delete_profile(storename):
    'Удаляем профиль'
    kill_chrome()  # Перед удалением киляем хром
    storefolder = store.options('storefolder')
    profilepath = os.path.abspath(
        os.path.join(storefolder, 'puppeteer', storename))
    shutil.rmtree(profilepath)
示例#9
0
 def wrapper(self, *args, **kwargs):
     '''decorator для безопасного запуска функции не падает в случае ошибки, а пишет в лог и возвращяет default=None
     параметры предназначенные декоратору, и не передаются в вызываемую функцию:
     default: возвращаемое в случае ошибки значение'''
     default = kwargs.pop('default', None)
     if len(args) > 0 and args[0] == '':
         return default
     # Готовим строку для лога
     log_string = f'call: {getattr(func,"__name__","")}({", ".join(map(repr,args))}, {", ".join([f"{k}={repr(v)}" for k,v in kwargs.items()])})'
     if str(store.options('log_full_eval_string')) == '0':
         log_string = log_string if len(
             log_string
         ) < 200 else log_string[:100] + '...' + log_string[-100:]
         if 'password' in log_string:
             log_string = log_string.split(
                 'password')[0] + 'password ....'
     log_string = log_string.encode('cp1251', errors='ignore').decode(
         'cp1251', errors='ignore')  # Убираем всякую хрень
     try:
         res = func(self, *args, **kwargs)  # pylint: disable=not-callable
         logging.info(f'{log_string} OK')
         return res
     except Exception:
         logging.info(
             f'{log_string} fail: {"".join(traceback.format_exception(*sys.exc_info()))}'
         )
         return default
示例#10
0
def clear_cache(storename):
    'Очищаем папку с кэшем профиля чтобы не разрастался'
    #return  # С такой очисткой оказывается связаны наши проблемы с загрузкой
    storefolder = store.options('storefolder')
    profilepath = os.path.abspath(
        os.path.join(storefolder, 'puppeteer', storename))
    shutil.rmtree(os.path.join(profilepath, 'Cache'), ignore_errors=True)
    shutil.rmtree(os.path.join(profilepath, 'Code Cache'), ignore_errors=True)
示例#11
0
文件: stock.py 项目: artyl/mbplugin
def get_balance(login, password, storename=None):
    result = {}
    session = store.Session(
        storename)  # Используем костылем для хранения предыдущих данных
    # если у нас еще нет переменной для истории - создаем (грязный хак - не делайте так, а если делаете - не пользуйтесь этой сессией для хождения в инет, а только для сохранения):
    session.session.params['history'] = session.session.params.get(
        'history', [])
    data = store.read_stocks(login.lower())
    stocks = data['stocks']
    remain = data['remain']
    currenc = data['currenc']
    res_data = count_all_scocks_multithread(stocks, remain, currenc)
    session.session.params['history'].append({
        'timestamp': time.time(),
        'data': res_data
    })  # Полученное значение сразу добавляем в хвост истории
    if store.options('stock_fulllog'):
        fulllog = '\n'.join(
            f'{time.strftime("%Y.%m.%d %H:%M:%S",time.localtime())}\t{i["security"]}\t{i["price"]}\t{i["currency"]}\t{i["cnt"]}\t{round(i["value"],2)}\t{round(i["value_priv"],2)}'
            for i in res_data)
        with open(
                os.path.join(store.options('loggingfolder'),
                             f'stock_{login}.log'), 'a') as f_log:
            f_log.write(fulllog + '\n')
    # Полная информация по стокам
    result['Stock'] = '\n'.join([
        f'{i["security"]+"("+i["currency"]+")":10} : {round(i["value_priv"],2):9.2f} {currenc}'
        for i in res_data
    ]) + '\n'
    result['UslugiOn'] = len(res_data)
    # Берем два последних элемента, а из них берем первый т.е. [1,2,3][-2:][0] -> 2 а [3][-2:][0] -> 3 чтобы не морочаться с проверкой есть ли предпоследний
    prev_data = session.session.params['history'][-2:][0][
        'data']  # TODO подумать какой из истории брать для вычисления. Пока беру предыдущий
    hst = {i['security']: i['value_priv'] for i in prev_data}
    result['UslugiList'] = '\n'.join([
        f'{i["security"]:5}({i["currency"]}) {i["value_priv"]-hst.get(i["security"],i["value_priv"]):+.2f}\t{i["value_priv"]:.2f}'
        for i in res_data
    ])
    # Полная информация подправленная для показа в balance.html
    result['Balance'] = round(sum([i['value_priv'] for i in res_data]),
                              2)  # Сумма в заданной валюте
    result['Currenc'] = currenc  # Валюта
    session.session.params['history'] = session.session.params['history'][
        -100:]  # оставляем последние 100 значений чтобы не росло бесконечно
    session.save_session()
    return result
示例#12
0
 def send_balance(self):
     'Отправляем баланс'
     if self.updater is None or str(
             store.options('send_balance_changes',
                           section='Telegram')) == '0':
         return
     baltxt = prepare_balance('LASTCHANGE')
     self.send_message(text=baltxt, parse_mode=telegram.ParseMode.HTML)
示例#13
0
def prepare_balance(filter='FULL', params={}):
    """Prepare balance for TG."""
    try:
        baltxt = ''
        if store.options('tg_from', section='Telegram',
                         mainparams=params) == 'sqlite':
            baltxt = prepare_balance_sqlite(filter, params)
        else:
            baltxt = prepare_balance_mobilebalance(filter, params)
        if baltxt == '' and str(
                store.options('send_empty',
                              section='Telegram',
                              mainparams=params)) == '1':
            baltxt = 'No changes'
        return baltxt
    except Exception:
        exception_text = f'Ошибка: {"".join(traceback.format_exception(*sys.exc_info()))}'
        logging.error(exception_text)
        return 'error'
示例#14
0
 def __init__(self):
     api_token = store.options('api_token', section='Telegram').strip()
     request_kwargs = {}
     tg_proxy = store.options('tg_proxy', section='Telegram').strip()
     if tg_proxy.lower() == 'auto':
         request_kwargs['proxy_url'] = urllib.request.getproxies().get(
             'https', '')
     elif tg_proxy != '' and tg_proxy.lower() != 'auto':
         request_kwargs['proxy_url'] = tg_proxy
         # ??? Надо или не надо ?
         # request_kwargs['urllib3_proxy_kwargs'] = {'assert_hostname': 'False', 'cert_reqs': 'CERT_NONE'}
     self.updater = None
     if api_token != '' and str(
             store.options(
                 'start_tgbot',
                 section='Telegram')) == '1' and 'telegram' in sys.modules:
         try:
             logging.info(
                 f'Module telegram starting for id={self.auth_id()}')
             self.updater = Updater(api_token,
                                    use_context=True,
                                    request_kwargs=request_kwargs)
             logging.info(f'{self.updater}')
             dp = self.updater.dispatcher
             dp.add_handler(CommandHandler("id", self.get_id))
             dp.add_handler(CommandHandler("balance", self.get_balancetext))
             dp.add_handler(
                 CommandHandler("balancefile", self.get_balancefile))
             self.updater.start_polling()  # Start the Bot
             if str(store.options('send_empty', section='Telegram')) == '1':
                 self.send_message(text='Hey there!')
         except Exception:
             exception_text = f'Ошибка запуска telegram bot {"".join(traceback.format_exception(*sys.exc_info()))}'
             logging.error(exception_text)
     elif 'telegram' not in sys.modules:
         logging.info('Module telegram not found')
     elif api_token == '':
         logging.info('Telegtam api_token not found')
     elif str(store.options('start_tgbot', section='Telegram')) != '1':
         logging.info(
             'Telegtam bot start is disabled in mbplugin.ini (start_tgbot=0)'
         )
示例#15
0
def kill_chrome():
    '''Киляем дебажный хром если вдруг какой-то висит, т.к. народ умудряется запускать не только хром, то имя exe возьмем из пути '''
    chrome_executable_path = store.options('chrome_executable_path')
    pname = os.path.split(chrome_executable_path)[-1].lower()
    for p in psutil.process_iter():
        try:
            if p.name().lower(
            ) == pname and 'remote-debugging-port' in ''.join(p.cmdline()):
                p.kill()
        except Exception:
            pass
示例#16
0
def prepare_balance_sqlite(filter='FULL', params={}):
    'Готовим данные для отчета из sqlite базы'
    db = dbengine.dbengine(store.options('dbfilename', mainparams=params))
    table_format = store.options('tg_format',
                                 section='Telegram',
                                 mainparams=params).replace('\\t',
                                                            '\t').replace(
                                                                '\\n', '\n')
    # table_format = 'Alias,PhoneNumber,Operator,Balance'
    # Если формат задан как перечисление полей через запятую - переделываем под формат
    if re.match(r'^(\w+(?:,|\Z))*$', table_format.strip()):
        table_format = ' '.join(
            [f'{{{i}}}' for i in table_format.strip().split(',')])
    table = db.report()
    table = [i for i in table if i['Alias'] != 'Unknown']  # filter Unknown
    table.sort(
        key=lambda i: [i['NN'], i['Alias']])  # sort by NN, after by Alias
    table = filter_balance(table, filter, params)
    res = [table_format.format(**line) for line in table]
    return '\n'.join(res)
示例#17
0
 def data_collector(self):
     #self.do_logon(url=login_url, user_selectors=user_selectors)
     # По простому если не видим баланса - показываем капчу
     self.page_goto(login_url)
     self.page_waitForNavigation()
     if str(store.options('show_captcha')) == '1':
         if not self.page_evaluate(js_check_balance_str, default=False):
             pa.hide_chrome(hide=False, foreground=True)
             for cnt2 in range(int(store.options('max_wait_captcha'))):
                 _ = cnt2
                 if self.page_evaluate(js_check_balance_str, default=False):
                     break
                 self.sleep(1)
     self.wait_params(params=[
         {
             'name':
             'Balance',
             'jsformula':
             r"parseFloat(document.querySelector('div[class=balance-widget__amount]').innerText.replace(/[^\d\.,-]/g,'').replace(',','.'))",
         },
     ],
                      url='https://yoomoney.ru/actions')
示例#18
0
 def OnCommand(self, hwnd, msg, wparam, lparam):
     id = win32api.LOWORD(wparam)
     port = int(store.options('port', section='HttpServer'))
     if id == 1024:
         os.system(f'start http://localhost:{port}/report')
     if id == 1025:
         os.system(f'start http://localhost:{port}/log?lines=40')
     elif id == 1026:
         print("Goodbye")
         win32gui.DestroyWindow(self.hwnd)
         self.cmdqueue.put('STOP')
     else:
         print("Unknown command -", id)
示例#19
0
 def history(self, phone_number, operator, days=7, lastonly=1):
     'Генерирует исторические данные по номеру телефона'
     if days == 0:
         return []
     historysql = f'''select * from phones where phonenumber=? and operator=? and QueryDateTime>date('now','-'|| ? ||' day') order by QueryDateTime desc'''
     cur = self.cur.execute(historysql, [phone_number, operator, days])
     dbheaders = list(zip(*cur.description))[0]
     dbdata = cur.fetchall()
     dbdata_sets = [
         set(l) for l in zip(*dbdata)
     ]  # составляем список уникальных значений по каждой колонке
     dbdata_sets = [{
         i
         for i in l if str(i).strip() not in ['', 'None', '0.0', '0']
     } for l in dbdata_sets]  # подправляем косяки
     if len(dbdata_sets) == 0:  # Истории нет - возвращаем пустой
         return []
     qtimes_num = dbheaders.index('QueryDateTime')
     qtimes = [line[qtimes_num]
               for line in dbdata]  # Список всех времен получения баланса
     qtimes_max = {
         max([k for k in qtimes if k.startswith(j)])
         for j in {i.split()[0]
                   for i in qtimes}
     }  # Последние даты получения баланса за сутки
     table = []  # результат - каждая строчка словарь элементов
     fields = store.options('HoverHistoryFormat').split(',')
     # выкидываем неинтересные колонки Там где только нули и None
     fields = [
         i for i in fields
         if i in dbheaders and dbdata_sets[dbheaders.index(i)] != set()
     ]
     for line in dbdata:
         row = dict(zip(dbheaders, line))
         if str(lastonly) == '0' or row[
                 'QueryDateTime'] in qtimes_max:  # фильруем данные по qtimes_max
             table.append({k: row[k] for k in fields if k in row})
     return table[::int(store.options('SkipDay')) + 1]
示例#20
0
def fix_crash_banner(storename):
    'Исправляем Preferences чтобы убрать баннер Работа Chrome была завершена некорректно'
    storefolder = store.options('storefolder')
    fn_pref = os.path.abspath(
        os.path.join(storefolder, 'puppeteer', storename, 'Preferences'))
    if not os.path.exists(fn_pref):
        return  # Нет Preferences - выходим
    with open(fn_pref) as f:
        data = f.read()
    data1 = data.replace('"exit_type":"Crashed"',
                         '"exit_type":"Normal"').replace(
                             '"exited_cleanly":false', '"exited_cleanly":true')
    if data != data1:
        logging.info(f'Fix chrome crash banner')
        open(fn_pref, mode='w').write(data1)
示例#21
0
def recompile():
    pluginpath = os.path.abspath(os.path.split(sys.argv[0])[0])
    os.chdir(pluginpath)
    sys.path.insert(0,pluginpath)
    port = store.options('port', section='HttpServer')
    tmpl = open(os.path.join(pluginpath, '..\\jsmblhplugin\\_template_localweb.jsmb'), encoding='cp1251').read()

    for fn in glob.glob(os.path.join(pluginpath, '..\\plugin\\*.py')):
        if ('def' + ' get_balance(') in open(fn, encoding='utf8').read():
            plugin = os.path.splitext(os.path.split(fn)[1])[0]
            fl = 'p_' + plugin
            module = __import__(plugin, globals(), locals(), [], 0)
            data = tmpl.replace('{{pluginname}}', fl).replace('{{port}}', port)
            if hasattr(module,'icon'):
                data = re.sub(r'//\s*Icon\s*:\s*\S*', f'// Icon      : {module.icon}', data)
            plugin_name = os.path.join(pluginpath, '..\\jsmblhplugin', fl+'_localweb.jsmb')
            open(plugin_name, 'w').write(data)
示例#22
0
def view_log(param):
    try:
        lines = int(param['lines'][0])
    except Exception:
        lines = 100
    fn = store.options('logginghttpfilename')
    res = open(fn).readlines()[-lines:]
    for num in range(len(res)):
        #.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
        if ' ERROR ' in res[num]:
            res[num] = f'<span style="color:red;background-color:white">{res[num]}</span>'
        elif ' WARNING ' in res[num]:
            res[num] = f'<span style="color:yellow;background-color:white">{res[num]}</span>'
    return 'text/html; charset=cp1251', [
        '<html><head></head><body><pre>'
    ] + res + [
        '</pre><script>window.scrollTo(0,document.body.scrollHeight);</script></body></html>'
    ]
示例#23
0
 def wrapper(*args, **kwargs):
     'decorator:Обертка для функций, выполнение которых не влияет на результат, чтобы при падении они не портили остальное'
     # Готовим строку для лога
     default = kwargs.pop('default', None)
     log_string = f'call: {getattr(func,"__name__","")}({", ".join(map(repr,args))}, {", ".join([f"{k}={repr(v)}" for k,v in kwargs.items()])})'
     if str(store.options('log_full_eval_string')) == '0':
         log_string = log_string if len(
             log_string
         ) < 200 else log_string[:100] + '...' + log_string[-100:]
         if 'password' in log_string:
             log_string = log_string.split('password')[0] + 'password ....'
     try:
         res = func(*args, **kwargs)  # pylint: disable=not-callable
         logging.info(f'{log_string} OK')
         return res
     except Exception:
         logging.info(
             f'{log_string} fail: {"".join(traceback.format_exception(*sys.exc_info()))}'
         )
         return default
示例#24
0
 def send_subsribtions(self):
     'Отправляем подписки - это строки из ini вида:'
     'subscribtionXXX = id:123456 include:1111,2222 exclude:6666'
     if self.updater is None:
         return
     subscribtions = store.options('subscribtion',
                                   section='Telegram',
                                   listparam=True)
     for subscr in subscribtions:
         # id:123456 include:1111,2222 -> {'id':'123456','include':'1111,2222'}
         params = {
             k: v.strip()
             for k, v in [i.split(':', 1) for i in subscr.split(' ')]
         }
         baltxt = prepare_balance('LASTCHANGE', params)
         ids = [
             int(i) for i in params.get('id', '').split(',') if i.isdigit()
         ]
         self.send_message(text=baltxt,
                           parse_mode=telegram.ParseMode.HTML,
                           ids=ids)
示例#25
0
 def simple_app(self, environ, start_response):
     try:
         status = '200 OK'
         ct, text = 'text/html', []
         fn = environ.get('PATH_INFO', None)
         _, cmd, *param = fn.split('/')
         print(f'{cmd}, {param}')
         if cmd.lower(
         ) == 'getbalance':  # старый вариант оставлен пока для совместимости
             ct, text = getbalance_plugin(
                 'url', param)  # TODO !!! Но правильно все-таки через POST
         elif cmd.lower() == 'sendtgbalance':
             self.telegram_bot.send_balance()
         elif cmd.lower() == 'sendtgsubscriptions':
             self.telegram_bot.send_subsribtions()
         elif cmd.lower() == 'get':  # вариант через get запрос
             param = urllib.parse.parse_qs(environ['QUERY_STRING'])
             ct, text = getbalance_plugin('get', param)
         elif cmd.lower() == 'log':  # просмотр лога
             param = urllib.parse.parse_qs(environ['QUERY_STRING'])
             ct, text = view_log(param)
         elif cmd == '' or cmd == 'report':  # report
             if store.options('sqlitestore') == '1':
                 ct, text = getreport(param)
             else:
                 ct, text = 'text/html', HTML_NO_REPORT
         elif cmd == 'exit':  # exit cmd
             self.cmdqueue.put('STOP')
             text = ['exit']
         headers = [('Content-type', ct)]
         start_response(status, headers)
         return [line.encode('cp1251', errors='ignore') for line in text]
     except Exception:
         exception_text = f'Ошибка: {"".join(traceback.format_exception(*sys.exc_info()))}'
         logging.error(exception_text)
         headers = [('Content-type', 'text/html')]
         return ['<html>ERROR</html>'.encode('cp1251')]
示例#26
0
 def __init__(self):
     self.cmdqueue = queue.Queue()
     logging.basicConfig(filename=store.options('logginghttpfilename'),
                         level=store.options('logginglevel'),
                         format=store.options('loggingformat'))
     self.port = int(store.options('port', section='HttpServer'))
     self.host = store.options('host', section='HttpServer')
     with socket.socket() as sock:
         sock.settimeout(
             0.2)  # this prevents a 2 second lag when starting the server
         if sock.connect_ex((self.host, self.port)) == 0:
             logging.info(
                 f"Port {self.host}:{self.port} already in use, try restart."
             )
             try:
                 requests.Session().get(
                     f'http://{self.host}:{self.port}/exit', timeout=1)
                 time.sleep(3)  # Подождем пока сервер остановится
             except Exception:
                 pass
     if str(store.options('start_http', section='HttpServer')) != '1':
         logging.info(
             f'Start http server disabled in mbplugin.ini (start_http=0)')
         return
     with wsgiref.simple_server.make_server(
             self.host,
             self.port,
             self.simple_app,
             server_class=ThreadingWSGIServer,
             handler_class=Handler) as self.httpd:
         logging.info(f'Listening {self.host}:{self.port}....')
         threading.Thread(target=self.httpd.serve_forever,
                          daemon=True).start()
         if 'win32api' in sys.modules:  # Иконка в трее
             threading.Thread(target=lambda i=self.cmdqueue: tray_icon(i),
                              daemon=True).start()
         if 'telegram' in sys.modules:  # telegram bot (он сам все запустит в threading)
             self.telegram_bot = TelegramBot()
         # Запустили все остальное демонами и ждем, когда они пришлют сигнал
         self.cmdqueue.get()
         self.telegram_bot.stop()
         self.httpd.shutdown()
     logging.info(f'Shutdown server {self.host}:{self.port}....')
示例#27
0
文件: mts2.py 项目: artyl/mbplugin
    def data_collector(self):
        mts_usedbyme = store.options('mts_usedbyme')
        self.do_logon(url=login_url, user_selectors=user_selectors)

        # TODO close banner # document.querySelectorAll('div[class=popup__close]').forEach(s=>s.click())
        if self.login_ori != self.login and self.acc_num.isdigit(
        ):  # это финт для захода через другой номер
            # если заход через другой номер то переключаемся на нужный номер
            # TODO возможно с прошлого раза может сохраниться переключенный но вроде работает и так
            self.page_waitForSelector("[id=ng-header__account-phone_desktop]")
            self.responses = {
            }  # Сбрасываем все загруженные данные - там данные по материнскому телефону
            url_redirect = f'https://login.mts.ru/amserver/UI/Login?service=idp2idp&IDButton=switch&IDToken1=id={self.acc_num},ou=user,o=users,ou=services,dc=amroot&org=/users&ForceAuth=true&goto=https://lk.mts.ru'
            self.page_goto(url_redirect)
            # !!! Раньше я на каждой странице при таком заходе проверял что номер тот, сейчас проверяю только на старте
            for _ in range(10):
                numb = self.page_evaluate(
                    "document.getElementById('ng-header__account-phone_desktop').innerText"
                )
                if numb is not None and numb != '':
                    break
            else:
                return  # номера на странице так и нет - уходим
            logging.info(f'PHONE {numb}')
            if re.sub(r'(?:\+7|\D)', '', numb) != self.acc_num:
                return  # Если номер не наш - уходим

        # Для начала только баланс быстрым способом (может запаздывать)
        self.wait_params(params=[
            {
                'name': 'Balance',
                'url_tag': ['api/login/userInfo'],
                'jsformula': "parseFloat(data.userProfile.balance).toFixed(2)"
            },
            # Закрываем банеры (для эстетики)
            {
                'name': '#banner1',
                'url_tag': ['api/login/userInfo'],
                'jsformula':
                "document.querySelectorAll('mts-dialog div[class=popup__close]').forEach(s=>s.click())",
                'wait': False
            },
        ])

        # Потом все остальное
        res1 = self.wait_params(params=[
            {
                'name': 'TarifPlan',
                'url_tag': ['api/login/userInfo'],
                'jsformula': "data.userProfile.tariff"
            },
            {
                'name': 'UserName',
                'url_tag': ['api/login/userInfo'],
                'jsformula': "data.userProfile.displayName"
            },
            {
                'name': 'Balance',
                'url_tag': ['for=api/accountInfo/mscpBalance'],
                'jsformula': "parseFloat(data.data.amount).toFixed(2)"
            },
            {
                'name': 'Balance2',
                'url_tag': ['for=api/cashback/account'],
                'jsformula': "parseFloat(data.data.balance).toFixed(2)"
            },
            {
                'name': '#counters',
                'url_tag': ['for=api/sharing/counters'],
                'jsformula': "data.data.counters"
            },
        ])
        if '#counters' in res1 and type(
                res1['#counters']) == list and len(res1['#counters']) > 0:
            counters = res1['#counters']
            # Минуты
            calling = [i for i in counters if i['packageType'] == 'Calling']
            if calling != []:
                unit = {
                    'Second': 60,
                    'Minute': 1
                }.get(calling[0]['unitType'], 1)
                nonused = [
                    i['amount'] for i in calling[0]['parts']
                    if i['partType'] == 'NonUsed'
                ]
                usedbyme = [
                    i['amount'] for i in calling[0]['parts']
                    if i['partType'] == 'UsedByMe'
                ]
                if nonused != []:
                    self.result['Min'] = int(nonused[0] / unit)
                if usedbyme != []:
                    self.result['SpendMin'] = int(usedbyme[0] / unit)
            # SMS
            messaging = [
                i for i in counters if i['packageType'] == 'Messaging'
            ]
            if messaging != []:
                nonused = [
                    i['amount'] for i in messaging[0]['parts']
                    if i['partType'] == 'NonUsed'
                ]
                usedbyme = [
                    i['amount'] for i in messaging[0]['parts']
                    if i['partType'] == 'UsedByMe'
                ]
                if (mts_usedbyme == '0' or self.login
                        not in mts_usedbyme.split(',')) and nonused != []:
                    self.result['SMS'] = int(nonused[0])
                if (mts_usedbyme == '1' or self.login
                        in mts_usedbyme.split(',')) and usedbyme != []:
                    self.result['SMS'] = int(usedbyme[0])
            # Интернет
            internet = [i for i in counters if i['packageType'] == 'Internet']
            if internet != []:
                unitMult = settings.UNIT.get(internet[0]['unitType'], 1)
                unitDiv = settings.UNIT.get(store.options('interUnit'), 1)
                nonused = [
                    i['amount'] for i in internet[0]['parts']
                    if i['partType'] == 'NonUsed'
                ]
                usedbyme = [
                    i['amount'] for i in internet[0]['parts']
                    if i['partType'] == 'UsedByMe'
                ]
                if (mts_usedbyme == '0' or self.login
                        not in mts_usedbyme.split(',')) and nonused != []:
                    self.result['Internet'] = round(
                        nonused[0] * unitMult / unitDiv, 2)
                if (mts_usedbyme == '1' or self.login
                        in mts_usedbyme.split(',')) and usedbyme != []:
                    self.result['Internet'] = round(
                        usedbyme[0] * unitMult / unitDiv, 2)

        self.page_goto('https://lk.mts.ru/uslugi/podklyuchennye')
        res2 = self.wait_params(params=[{
            'name':
            '#services',
            'url_tag': ['for=api/services/list/active$'],
            'jsformula':
            "data.data.services.map(s=>[s.name,!!s.subscriptionFee.value?s.subscriptionFee.value:0])"
        }])
        try:
            services = sorted(res2['#services'], key=lambda i: (-i[1], i[0]))
            free = len([
                a for a, b in services
                if b == 0 and (a, b) != ('Ежемесячная плата за тариф', 0)
            ])
            paid = len([a for a, b in services if b != 0])
            paid_sum = round(sum([b for a, b in services if b != 0]), 2)
            self.result['UslugiOn'] = f'{free}/{paid}({paid_sum})'
            self.result['UslugiList'] = '\n'.join(
                [f'{a}\t{b}' for a, b in services])
        except Exception:
            logging.info(
                f'Ошибка при получении списка услуг {"".join(traceback.format_exception(*sys.exc_info()))}'
            )

        # Идем и пытаемся взять инфу со страницы https://lk.mts.ru/obshchiy_paket
        # Но только если телефон в списке в поле mts_usedbyme или для всех телефонов если там 1
        if mts_usedbyme == '1' or self.login in mts_usedbyme.split(
                ',') or self.acc_num.lower().startswith('common'):
            self.page_goto('https://lk.mts.ru/obshchiy_paket')
            res3 = self.wait_params(params=[{
                'name':
                '#checktask',
                'url_tag': ['for=api/Widgets/GetUserClaims', '/longtask/'],
                'jsformula':
                "data.result"
            }])
            try:
                if 'RoleDonor' in str(
                        res3
                ):  # Просто ищем подстроку во всем json вдруг что-то изменится
                    logging.info(f'mts_usedbyme: RoleDonor')
                    res4 = self.wait_params(params=[{
                        'name':
                        '#donor',
                        'url_tag': [
                            'for=api/Widgets/AvailableCountersDonor$',
                            '/longtask/'
                        ],
                        'jsformula':
                        "data.result"
                    }])
                    # acceptorsTotalConsumption - иногда возвращается 0 приходится считать самим
                    # data = {i['counterViewUnit']:i['groupConsumption']-i['acceptorsTotalConsumption'] for i in res4['#donor']}
                    data = {
                        i['counterViewUnit']: i['groupConsumption'] - sum([
                            j.get('consumption', 0)
                            for j in i.get('acceptorsConsumption', [])
                        ])
                        for i in res4['#donor']
                    }
                if 'RoleAcceptor' in str(res3):
                    logging.info(f'mts_usedbyme: RoleAcceptor')
                    res4 = self.wait_params(params=[{
                        'name':
                        '#acceptor',
                        'url_tag': [
                            'for=api/Widgets/AvailableCountersAcceptor',
                            '/longtask/'
                        ],
                        'jsformula':
                        "data.result.counters"
                    }])
                    data = {
                        i['counterViewUnit']: i['consumption']
                        for i in res4['#acceptor']
                    }
                if 'RoleDonor' in str(res3) or 'RoleAcceptor' in str(res3):
                    logging.info(f'mts_usedbyme collect: data={data}')
                    if 'MINUTE' in data:
                        self.result['SpendMin'] = data["MINUTE"]
                    if 'ITEM' in data:
                        self.result['SMS'] = data["ITEM"]
                    if 'GBYTE' in data:
                        self.result['Internet'] = data["GBYTE"]
                # Спецверсия для общего пакета, работает только для Donor
                if self.acc_num.lower().startswith('common'):
                    if 'RoleDonor' in str(res3):
                        # потребление и остаток
                        cdata_charge = {
                            i['counterViewUnit']: i['groupConsumption']
                            for i in res4['#donor']
                        }
                        сdata_rest = {
                            i['counterViewUnit']:
                            i['counterLimit'] - i['groupConsumption']
                            for i in res4['#donor']
                        }
                        self.result['Min'] = сdata_rest[
                            "MINUTE"]  # осталось минут
                        self.result['SpendMin'] = cdata_charge[
                            "MINUTE"]  # Потрачено минут
                        if 'rest' in self.acc_num:
                            self.result['SMS'] = сdata_rest[
                                "ITEM"]  # остатки по инету и SMS
                            self.result['Internet'] = сdata_rest["GBYTE"]
                        else:
                            self.result['SMS'] = cdata_charge[
                                "ITEM"]  # расход по инету и SMS
                            self.result['Internet'] = cdata_charge["GBYTE"]
                        logging.info(
                            f'mts_usedbyme common collect: сdata_rest={сdata_rest} cdata_charge={cdata_charge}'
                        )
                    else:  #  Со страницы общего пакета не отдали данные, чистим все, иначе будут кривые графики. ТОЛЬКО для common
                        raise RuntimeError(
                            f'Страница общего пакета не возвращает данных')
            except:
                logging.info(
                    f'Ошибка при получении obshchiy_paket {"".join(traceback.format_exception(*sys.exc_info()))}'
                )
                if self.acc_num.lower().startswith('common'):
                    self.result = {
                        'ErrorMsg':
                        'Страница общего пакета не возвращает данных'
                    }
示例#28
0
import os, sys, re, glob
import settings, store

pluginpath = os.path.abspath(os.path.split(sys.argv[0])[0])
os.chdir(pluginpath)
sys.path.insert(0, pluginpath)
port = store.options('port', section='HttpServer')
tmpl = open(os.path.join(pluginpath,
                         '..\\jsmblhplugin\\_template_localweb.jsmb'),
            encoding='cp1251').read()

for fn in glob.glob(os.path.join(pluginpath, '..\\plugin\\*.py')):
    if ('def' + ' get_balance(') in open(fn, encoding='utf8').read():
        plugin = os.path.splitext(os.path.split(fn)[1])[0]
        fl = 'p_' + plugin
        module = __import__(plugin, globals(), locals(), [], 0)
        data = tmpl.replace('{{pluginname}}', fl).replace('{{port}}', port)
        if hasattr(module, 'icon'):
            data = re.sub(r'//\s*Icon\s*:\s*\S*',
                          f'// Icon      : {module.icon}', data)
        plugin_name = os.path.join(pluginpath, '..\\jsmblhplugin',
                                   fl + '_localweb.jsmb')
        open(plugin_name, 'w').write(data)
示例#29
0
 def do_logon(self, url=None, user_selectors=None):
     '''Делаем заход в личный кабинет/ проверяем не залогинены ли уже
     На вход передаем словарь селекторов и скриптов который перекроет действия по умолчанию
     Если какой-то из шагов по умолчанию хотим пропустить, передаем пустую строку
     Смотрите актуальное описание напротив параметров в коментариях
     Чтобы избежать ошибок - копируйте названия параметров'''
     selectors = default_logon_selectors.copy()
     if url is None:
         url = self.login_url
     if user_selectors is None:
         user_selectors = self.user_selectors if user_selectors is not None else {}
     # проверяем что все поля из user_selectors есть в селектор (если не так то скорее всего опечатка и надо сигналить)
     if set(user_selectors) - set(selectors) != set():
         logging.error(
             f'Не все ключи из user_selectors есть в selectors. Возможна опечатка, проверьте {set(user_selectors)-set(selectors)}'
         )
     selectors.update(user_selectors)
     if url is not None:  # Иногда мы должны сложным путем попасть на страницу - тогда указываем url=None
         self.page_goto(url)
     self.page_waitForNavigation()
     self.sleep(1)
     for countdown in range(self.wait_loop):
         if self.page_evaluate(selectors['chk_lk_page_js']):
             logging.info(f'Already login')
             break  # ВЫХОДИМ ИЗ ЦИКЛА - уже залогинины
         if self.page_evaluate(selectors['chk_login_page_js']):
             logging.info(f'Login')
             self.page_evaluate(
                 selectors['before_login_js']
             )  # Если задано какое-то действие перед логином - выполняем
             self.page_waitForSelector(
                 selectors['login_selector'])  # Ожидаем наличия поля логина
             self.page_evaluate(
                 selectors['login_clear_js'])  # очищаем поле логина
             self.page_type(selectors['login_selector'], self.login,
                            {'delay': 10})  # вводим логин
             if (self.page_evaluate(selectors['chk_submit_after_login_js'],
                                    default=False)
                 ):  # Если нужно после логина нажать submit
                 self.page_click(
                     selectors['submit_after_login_selector'])  # либо click
                 self.page_evaluate(
                     selectors['submit_after_login_js'])  # либо через js
                 self.page_waitForSelector(
                     selectors['password_selector']
                 )  # и ждем появления поля с паролем
                 self.sleep(1)
             self.page_evaluate(
                 selectors['password_clear_js'])  # очищаем поле пароля
             self.page_type(selectors['password_selector'], self.password,
                            {'delay': 10})  # вводим пароль
             if self.page_evaluate(
                     selectors['remember_checker'], default=False
             ):  # Если есть невыставленный check remember me
                 self.page_evaluate(
                     selectors['remember_js'])  # выставляем его
                 self.page_click(selectors['remember_selector'],
                                 {'delay': 10})
             self.sleep(int(selectors['pause_press_submit']))
             self.page_click(
                 selectors['submit_selector'])  #  нажимаем на submit form
             self.page_evaluate(
                 selectors['submit_js']
             )  # либо через js (на некоторых сайтах один из вариантов не срабатывает)
             self.page_waitForNavigation()  # ждем отработки нажатия
             self.sleep(1)
             if self.page_evaluate(selectors['chk_lk_page_js']):
                 logging.info(f'Logged on')
                 break  # ВЫХОДИМ ИЗ ЦИКЛА - залогинились
             self.sleep(1)
             # Проверяем - это не капча ?
             if self.page_evaluate(selectors['captcha_checker'], False):
                 # Если стоит флаг показывать капчу то включаем видимость хрома и ждем заданное время
                 if str(store.options('show_captcha')) == '1':
                     logging.info('Show captcha')
                     hide_chrome(hide=False, foreground=True)
                     self.page_evaluate(selectors['captcha_focus'])
                     for cnt2 in range(
                             int(store.options('max_wait_captcha'))):
                         _ = cnt2
                         if not self.page_evaluate(
                                 selectors['captcha_checker'], False):
                             break  # ВЫХОДИМ ИЗ ЦИКЛА - капчи на странице больше нет
                         self.sleep(1)
                     else:  # Капчу так никто и не ввел
                         logging.error(
                             f'Show captcha timeout. A captcha appeared, but no one entered it'
                         )
                         raise RuntimeError(
                             f'A captcha appeared, but no one entered it')
                 else:  # Показ капчи не зададан выдаем ошибку и завершаем
                     logging.error(f'Captcha appeared')
                     raise RuntimeError(f'Captcha appeared')
             else:
                 # Никуда не попали и это не капча
                 logging.error(f'Unknown state')
                 raise RuntimeError(f'Unknown state')
             break  # ВЫХОДИМ ИЗ ЦИКЛА
         if countdown == self.wait_and_reload:
             # так и не дождались - пробуем перезагрузить и еще подождать
             self.page_reload('Unknown page try reload')
         self.sleep(1)
示例#30
0
 def browser_launch(self):
     hide_chrome_flag = str(store.options(
         'show_chrome')) == '0' and store.options('logginglevel') != 'DEBUG'
     storefolder = store.options('storefolder')
     user_data_dir = os.path.join(storefolder, 'puppeteer')
     profile_directory = self.storename
     chrome_executable_path = store.options('chrome_executable_path')
     if not os.path.exists(chrome_executable_path):
         chrome_paths = [
             p for p in settings.chrome_executable_path_alternate
             if os.path.exists(p)
         ]
         if len(chrome_paths) == 0:
             logging.error('Chrome.exe not found')
             raise RuntimeError(f'Chrome.exe not found')
         chrome_executable_path = chrome_paths[0]
     logging.info(f'Launch chrome from {chrome_executable_path}')
     launch_config = {
         'headless':
         False,
         'ignoreHTTPSErrors':
         True,
         'defaultViewport':
         None,
         'handleSIGINT':
         False,  # need for threading (https://stackoverflow.com/questions/53679905)
         'handleSIGTERM':
         False,
         'handleSIGHUP':
         False,
         # TODO хранить параметр в ini
         'executablePath':
         chrome_executable_path,
         'args': [
             f"--user-data-dir={os.path.abspath(user_data_dir)}",
             f"--profile-directory={profile_directory}",
             '--wm-window-animations-disabled',
             '--no-sandbox',
             '--disable-setuid-sandbox',
             '--disable-dev-shm-usage',
             '--disable-accelerated-2d-canvas',
             '--no-first-run',
             '--no-zygote',
             '--log-level=3',  # no logging                 
             #'--single-process', # <- this one doesn't works in Windows
             '--disable-gpu',
             "--window-position=-2000,-2000"
             if hide_chrome_flag else "--window-position=80,80",
             "--window-size=800,900"
         ],
     }
     if store.options('proxy_server').strip() != '':
         launch_config['args'].append(
             f'--proxy-server={store.options("proxy_server").strip()}')
     fix_crash_banner(self.storename)
     self.browser = self.loop.run_until_complete(
         pyppeteer.launch(launch_config))
     if hide_chrome_flag:
         hide_chrome()
     pages = self.loop.run_until_complete(self.browser.pages())
     for pg in pages[1:]:
         self.loop.run_until_complete(
             pg.close())  # Закрываем остальные страницы, если вдруг открыты
     self.page = pages[0]  # await browser.newPage()
     if self.async_response_worker is not None:
         self.page.on(
             "response",
             self.async_response_worker)  # вешаем обработчик на страницы
     if self.async_disconnected_worker is not None:
         self.browser.on("disconnected", self.async_disconnected_worker
                         )  # вешаем обработчик закрытие браузера