Даже не знаю с чего начать писать. Будет быдлокод, много. Не трогайте меня! И код! Я учусь. Захотел поделиться впервые чем-то и выбрал именно Хабр, как самую популярную для таких тем площадку, потому как сам искал тут информацию по мере необходимости, но страшно немного, что утопят в критике. Это самая первая статья тут и, вообще, по моему опыту создания чего-либо. Я лишь хочу для себя движения, а для вас поделиться своей радостью. Так как я сделал свой велосипед (два колеса и всё такое =D). Когда я искал информацию, я собирал её по частям, а выкладываю как готовое пособие, небольшой, скромный гайдик, как я и что сделал.

Итак имеем: работу по найму в IT отделе. Желание ничего не делать и делать вид, что что-то делается, хотя иногда желание поработать есть, но только над интересными задачами. На всякие принеси, подай, перезагрузи есть умы попроще. Появляется периодически идея в поиске и изучении как работают отметки в СКУД (система контроля и управления доступом, для тех кто не в курсе). База данных, просто, гигантская. Программный комплекс интеллект постарался.

Сам по себе интеллект, разработанный в России, жутко кривой от версии к версии, лучше на него даже не дышать. Это такой мощный костыль, который может работать с другими, разными костылями одновременно из одного "командного центра". Контроллеры у нас APDA21, это и есть наш подключенный в интеллект СКУД Tempo Reale. Устанавливали это всё ещё до моего прихода, поэтому что зачем и почему я не отвечу вам. Имеем, что имеем.

База данных у интеллекта состоит, наверное, из сотни таблиц. И поиск по содержимому мне на ум не приходил (даже не помню почему), я иногда рандомно открывал несколько таблиц и забивал, но однажды я нашел заветную таблицу, которая вбирает в себя все логи контроллеров, а их у нас, забыл сказать, около 50. Тот человек, который следит за нашими проходами сидит далеко, смотрит сформированные таблицы по запросам в WEB интерфейсе системы отчётов. Первым делом надо было установить человеческий "обозреватель" баз данных. Я не могу использовать эту MSSQL, я подключил её в Navicat и первое время мы меняли время на давних отметках.

Почему меняли? Почему не написали плановую задачу? Первое время пользовались AnyDesk или просили того кто на работе поставить отметку. Это не удобно т.к. в таблице есть колонка date, time и , вроде, time2, помимо этого там ещё куча колонок, то есть с телефона или тачпадом на ноуте не шик как удобно двигать постоянно ползунки, а ещё надо найти строчку с подходящим по критериям давности временем для замены и всё эти 3 столбца содержат не то что должны. во всех формат записи, типа: (dd.mm.yyyy hh:mm:ss:000), почему не взять из одного столбца одну и туже информацию? Ну это интеллект, я много смешного могу рассказать о работе с ним. Плановая задача отпадает на стадии замены символов. Слишком сложно, а там ещё есть уникальный key в последнем столбце.

Вот так и продолжалось N-ное количество времени и предложение по созданию бота уже прозвучало к тому времени, но знаний как, что, зачем, почему не было. Работало это всё в том числе потому, что территория у предприятия большая и ужасно распределена. Можно достаточно долго идти от здания к зданию до другого отдела, а звонки быстро переводятся с рабочего на сотовый и получить задачу оперативно не составляло труда, делегировать задачи тоже.

Появилось желание изучить наконец как сделать бота. Это темный лес. Оказывается надо Pyton, это тоже тёмный лес. Уже сотни вкладок с конструкторами бесполезными, платными, со страницами кода и... пришлось качать PyCharm Community. Устанавливаем библиотеки по мере их необходимости подключения, ищем отдельно код для работы с MSSQL, ох ненавижу..., для создания меню внутри бота, для генерации случайной последовательности символов на языке который в первый раз используем, для генерации правильной строки текущего времени, смотрим синтаксисы if, elif, позже уже try, except и так далее, то есть всё по крупицам в новом для нас языке. То, что не надо писать ";" в конце прям очень зашло.

Я не сохранил первую тестовую версию программы, где был только я без коллег и одна кнопка, но радости было... Вы представляете? Нажимаете в приложении для обмена сообщениями боту кнопку, а он на работе отмечает вас в тот же момент времени где вы его попросите. Важная ремарочка ещё: самым простым способом открыть дверь без отметки - было сделать открывание двери по скрипту с HotKey на устройстве где запущен бот. Бот как бы получает команду из чата нажать кнопки в Windows (ctrl+alt+Shift+Q), а в Windows это сочетание хватает Intellect и выполняет скрипт на открытие двери, как будто поднесли карту. И вот наш первый код:

import telebot
import random
import pypyodbc
import keyboard
import datetime
from telebot import types

access_token = "аццесс токен намбер фром фатхер бот"
bot = telebot.TeleBot(access_token)
connectdb = pypyodbc.connect('Driver={SQL Server};Server=yourserver;Database=yourdatabase;')
cursor = connectdb.cursor()
bot.send_message(3333333, '•Бот перезапущен после краша. /start')bot.send_message(4444444, '•Бот перезапущен после краша. /start') # \n Обновления:\n•Защита от бла бла 
@bot.message_handler(commands=["start"])
def welcome(m):
    keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True)
    keyboard.add([types.KeyboardButton(name) for name in ['/start']])
    keyboard.add([types.KeyboardButton(name) for name in ['Иван', 'Пётр']])
    keyboard.add(*[types.KeyboardButton(name) for name in ['Точка прохода', 'Открыть дверь']])
    if m.chat.id == 333333333 or m.chat.id == 444444444:
        bot.send_message(m.chat.id, 'Усраться, ты вспомнил обо мне. Приступим?', reply_markup=keyboard)
@bot.message_handler(content_types=['text'])
def inline_key(a):
    global door
    password = ''
    chars = 'ABCDEF1234567890'
    for i in range(8):
        password += random.choice(chars)
    password = password + '-1DA7-EB11-8C16-001E0B92DEEE'
    timeintel = datetime.datetime.now()
    timeintel = timeintel.strftime('%Y-%m-%d %H:%M:%S.000')
    try:
        if door == '':
            door = ''
    except:
        door = '1.2.2'
    if a.text == "Открыть дверь":
        markup_inline = types.InlineKeyboardMarkup()
        item_5oo = types.InlineKeyboardButton(text='Открыть такую-то дверь', callback_data='o122k')
        item_5zo = types.InlineKeyboardButton(text='Открыть ...', callback_data='o121k')
        item_4oo = types.InlineKeyboardButton(text='Открыть ...', callback_data='o1401k')
        item_4zo = types.InlineKeyboardButton(text='Открыть ...', callback_data='o1402k')
        item_26ko = types.InlineKeyboardButton(text='Открыть ...', callback_data='o26k')
        markup_inline.add(item_5oo)
        markup_inline.add(item_5zo)
        markup_inline.add(item_4oo)
        markup_inline.add(item_4zo)
        markup_inline.add(item_26ko)
        bot.send_message(a.chat.id, 'Чё открываем? Окно в Америку =D', reply_markup=markup_inline)
    if a.text == "Точка прохода":
        markup_inline = types.InlineKeyboardMarkup()
        item_5o = types.InlineKeyboardButton(text='Здеся', callback_data='p122')
        item_5z = types.InlineKeyboardButton(text='Тут-то', callback_data='p121')
        item_4o = types.InlineKeyboardButton(text='Там-то', callback_data='p1401')
        item_4z = types.InlineKeyboardButton(text='Где-то', callback_data='p1402')
        markup_inline.add(item_5o, item_5z)
        markup_inline.add(item_4o, item_4z)
        bot.send_message(a.chat.id, 'Сделайте выбор', reply_markup=markup_inline)
    if door == '1.2.1' or door == '1.2.2' or door == '1.40.1' or door == '1.40.2':
        if a.text == "Пётр":
            cursor.execute("insert into dbo.protocol(objtype,objid,action,region_id,param0,param1,param2,param3,"
                           "date,time,time2,owner,pk,operator_id) values ('APDA21_READER', '" + door + "', "
                            "'ACCESS_IN', '', ' Петров Петр Петрович', '1111', '', '(111) 20673', '" + timeintel +
                           "', '" + timeintel + "', '" + timeintel + "', 'INTELLEKT','" + password + "', '875')")
            connectdb.commit()
            print(a.text + ', ' + door + ', ' + password + ', ' + timeintel)
            bot.send_message(a.chat.id, a.text + ', ' + door + ', Сработало! ' + password + ' ' + timeintel)
        if a.text == "Иван":
            cursor.execute("insert into dbo.protocol(objtype,objid,action,region_id,param0,param1,param2,param3,"
                           "date,time,time2,owner,pk,operator_id) values ('APDA21_READER', '" + door + "', "
                            "'ACCESS_IN', '', ' Иванов Иван Иванович', '2222', '', '(111) 15223', '" + timeintel +
                           "', '" + timeintel + "', '" + timeintel + "', 'INTELLEKT','" + password + "', '875')")
            connectdb.commit()
            print(a.text + ', ' + door + ', ' + password + ', ' + timeintel)
            bot.send_message(a.chat.id, a.text + ', ' + door + ', Сработало! ' + password + ' ' + timeintel)
@bot.callback_query_handler(func=lambda call: True)
def callback_inline(call):
    global door
    timelog = datetime.datetime.now()
    timelog = timelog.strftime('%Y-%m-%d %H:%M:%S.000')
    if call.data == 'p121':
        bot.send_message(call.message.chat.id, 'Допустим 2 корпус выход')
        door = '1.2.1'
    elif call.data == 'p122':
        bot.send_message(call.message.chat.id, 'Допустим 2 корпус вход')
        door = '1.2.2'
    elif call.data == 'p1401':
        bot.send_message(call.message.chat.id, 'Допустим 3 этаж')
        door = '1.40.1'
    elif call.data == 'p1402':
        bot.send_message(call.message.chat.id, 'Допустим 4 этаж')
        door = '1.40.2'
elif call.data == 'o121k':
    keyboard.send("ctrl+alt+shift+Q")
    bot.send_message(call.message.chat.id, 'Открыто')
    print('допустим 2к вых', ' + timelog)
    call.data = 'null'
elif call.data == 'o122k':
    keyboard.send("ctrl+alt+shift+W")
    bot.send_message(call.message.chat.id, 'Открыто')
    print('допустим 2к вх, ' + timelog)
    call.data = 'null'

bot.polling()
    

Всё менять лень: урезал повторяющийся код с 200 до 100 строк, изменил немного данные чужие на вымышленные только лишь для общего понимания кода. Как я и говорил быдлокод. Я понимаю куда программу совершенствовать, но чтобы понять как правильно это сделать и не наломать других дров надо больше практики, знаний, а я хочу быстрее, но идеи по улучшению пришли быстро и я принялся делать из того, что могу:

import telebot
import random 
import pypyodbc
import keyboard
import datetime
import config  # Импорт config.py
import time
from telebot import types  # MENU Keyboard Bot

bot = telebot.TeleBot(config.token)
connectdb = pypyodbc.connect(config.database)

# bot.send_message(11111111, config.message)
# bot.send_message(22222222, config.message)
# bot.send_message(33333333, config.message)
# bot.send_message(44444444, config.message)

# Тут работаем с командой start
@bot.message_handler(commands=['start'])
def welcome_start(m):
    keybdmain = types.ReplyKeyboardMarkup(resize_keyboard=True)
    keybdmain.add(*[types.KeyboardButton(name) for name in ['Быстрая отметка']])
    keybdmain.add(*[types.KeyboardButton(name) for name in ['Отметить', 'Открыть']])
    keybdmain.add(*[types.KeyboardButton(name) for name in ['Остальная шляпа']])
    if m.chat.id in config.users:
        bot.send_message(m.chat.id, 'Усраться, ты вспомнил обо мне? Приступим!', reply_markup=keybdmain)
    else:
        bot.send_message(m.chat.id, 'Извините, до свидания. Бот не для вас!')

@bot.message_handler(content_types=["text"])
def content_text(message):
    if message.text == 'Верни в зад':
        config.door = ''
        config.switcher = ''
        config.booldoor = ''
        keybdmain = types.ReplyKeyboardMarkup(resize_keyboard=True)
        keybdmain.add(*[types.KeyboardButton(name) for name in ['Быстрая отметка']])
        keybdmain.add(*[types.KeyboardButton(name) for name in ['Отметить', 'Открыть']])
        keybdmain.add(*[types.KeyboardButton(name) for name in ['Остальная шляпа']])
        if message.chat.id in config.users:
            bot.send_message(message.chat.id, 'Усраться, ты вспомнил обо мне. Приступим?', reply_markup=keybdmain)

    if message.text == 'Открыть' and config.switcher == '':
        config.door = ''
        config.switcher = 'wronghole'
        keyboard2 = types.ReplyKeyboardMarkup(resize_keyboard=True)
        keyboard2.add(*[types.KeyboardButton(name) for name in ['4к5э осн', '4к5э зап', '4к4э осн']])
        keyboard2.add(*[types.KeyboardButton(name) for name in ['4к4э зап', 'Калитка']])
        keyboard2.add(*[types.KeyboardButton(name) for name in ['Ручной ввод']])
        keyboard2.add(*[types.KeyboardButton(name) for name in ['Верни в зад']])
        bot.send_message(message.chat.id, 'Где?', reply_markup=keyboard2)

    if message.text == 'Быстрая отметка' and config.switcher == '':
        config.door = ''
        config.switcher = 'check'
        keyboard2 = types.ReplyKeyboardMarkup(resize_keyboard=True)
        keyboard2.add(*[types.KeyboardButton(name) for name in ['4к5э осн', '4к5э зап', '4к4э осн']])
        keyboard2.add(*[types.KeyboardButton(name) for name in ['4к4э зап', '10п вход', '10п выход']])
        keyboard2.add(*[types.KeyboardButton(name) for name in ['2п вход', '2п выход', 'Ручной ввод']])
        keyboard2.add(*[types.KeyboardButton(name) for name in ['Верни в зад']])
        bot.send_message(message.chat.id, 'Выбери где?', reply_markup=keyboard2)

    if message.text == '4к5э осн':
        config.door = '1.2.2'
        config.key = 'A'
    if message.text == '4к5э зап':
        config.door = '1.2.1'
        config.key = 'B'
    if message.text == '4к4э осн':
        config.door = '1.40.1'
        config.key = 'C'
    if message.text == '4к4э зап':
        config.door = '1.40.2'
        config.key = 'D'
    if message.text == 'Калитка':
        config.key = 'K'
    if message.text == 'Ручной ввод':
        if config.booldoor == '':
            send = bot.send_message(message.chat.id, 'Из за ошибки не сработает. Введи номер! Если не понятно - спроси Петра что это.')
            config.booldoor = 'true'
        elif config.booldoor == 'true':
            config.booldoor = ''
    if config.booldoor == 'true' and message.text != 'Ручной ввод':
        config.door = message.text
        config.booldoor = ''

    if config.switcher == 'wronghole' and config.key != '':
        bot.send_message(message.chat.id, 'Пока не работает!')
        try:
            keyboard.send("ctrl+alt+shift+" + config.key)
            print("ctrl+alt+shift+" + config.key)
            bot.send_message(message.chat.id, 'Открыто!')
        except:
            bot.send_message(message.chat.id, 'Аларма, всё пошло по песте!')
        config.door = ''
        config.key = ''
        config.switcher = ''

    if config.switcher == 'check' and config.door != '':
        config.switcher = ''
        # Тут загенерили уникальный код
        password = ''
        chars = 'ABCDEF1234567890'
        for i in range(8):
            password += random.choice(chars)
        password = password + '-1DA7-EB11-8C16-001E0B92DEEE' 

        # Тут время установили на сейчас
        timeintel = datetime.datetime.now()
        timeintel = timeintel.strftime('%Y-%m-%d %H:%M:%S.000')

        # Тут выяснили кто есть кто
        if message.chat.id == 1111111111:
            params = [' Петров Пётр Петрвич', '2272', '(110) 11111']
        elif message.chat.id == 222222222:
            params = [' Иванов Иван Иванович', '640', '(111) 11111']
        elif message.chat.id == 333333333:
            params = [' Алексеев Алексей Алексеевич', '642', '(118) 12345']
        elif message.chat.id == 444444444:
            params = [' Дмитриев Дмитрий Дмитриевич', '1696', '(17) 33221']

        # Ну и пробуем отмечать
        try:
            cursor = connectdb.cursor()
            cursor.execute("insert into dbo.protocol(objtype,objid,action,region_id,param0,param1,param2,param3,date,time,time2,owner,pk,operator_id) values ('APDA21_READER', '" + config.door + "', 'ACCESS_IN', '', '" +params[0] + "', '" + params[1] + "', '', '" + params[2] + "', '" + timeintel + "', '" + timeintel + "', '" + timeintel + "', 'INTELLEKT','" + password + "', '875')")
            connectdb.commit()
            bot.send_message(message.chat.id, params[0] + ', ' + config.door + ', Сработало! ' + password + ' ' + timeintel)
            print(params[0] + ', ' + config.door + ', Сработало! ' + password + ' ' + timeintel)
        except:
            bot.send_message(message.chat.id, 'АЛАРМА ПЕСТА, что-то пошло не так!!!, ' + params[0] + ', ' + config.door + ', ' + password + ', ' + timeintel)
            print('АЛАРМА ПЕСТА, что-то пошло не так!!!, ' + params[0] + ', ' + config.door + ', ' + password + ', ' + timeintel) #не забыть дописать ошибку exception e
        config.door = ''
        config.key = ''
        config.switcher = ''
        bot.send_message(message.chat.id, 'ОЧЕНЬ ВАЖНО! Вернись в стартовое меню перед новой отметкой! Временное решение.')

    if message.text == 'Остальная шляпа':
        # ПОКА НИЧЕГО НЕ ГОТОВО, незабыть удалить и сделать
        i = random.randint(1, 4)
        if i == 1:
            bot.send_message(message.chat.id, 'У котов не руки, а лапки, а это значит отэбись пока от этого пункта меню!')
        elif i == 2:
            bot.send_message(message.chat.id, 'Гора эверест самая высокая, поэтому отэбись пока от этого пункта меню!')
        elif i == 3:
            bot.send_message(message.chat.id, 'Биткоин - это криптовалюта, а это значит отъебись пока от этого пункта меню!')
        elif i == 4:
            bot.send_message(message.chat.id, 'В Москве есть кремль, а это значит отэбись пока от этого пункта меню!')


while True:
    try:
        bot.polling(none_stop=True)
    except Exception as e:
        time.sleep(10)
        print(e)

Тут я уже изменил меньше, чтобы больше показать достоверность рабочего кода, но у меня на неопределенное время нет доступа к файлу config.py, поэтому извините, там меньше 10 строчек и сугубо личные данные. Тоже есть быдлокод, потому что написать вторую версию было важно с чистого листа, быстро, а уже потом исправлять и допиливать. Но когда я её доделаю уже не знаю. Это не на массового потребителя, а для своих людей и своих всё итак устраивает и работает, а у меня нынче нет на это времени, я уже занят другими делами. Самое важное нововведение в этом боте стал кусок кода в конце. У нас иногда пропадал интернет и происходило малозаметное переключение провайдера для пользователя, но даже одной секунды хватало боту для краша.

Вот как-то так удалось обмануть систему и получить небольшой, но дражайший опыт, которым я могу поделиться, потому что ещё недавно когда я искал информацию не было ничего подобного в моих поисковых запросах. Спасибо за внимание. Буду рад если статья окажется полезна вам или, просто, понравится сейчас или через год (не важно).

Комментарии (19)