def support(user_id, urgent=False): """ Handles every attempt to open support dialogue. Do not open if not urgent and not in working time """ time.sleep(1) if not urgent: # User trying to contact support in non working time if not 17 <= datetime.datetime.today().hour < 22 or datetime.datetime.today().isoweekday() in [6, 7]: buttons = [Button(title='Срочно', type='postback', payload='urgent')] fb_bot.send_button_message(user_id, messages.non_working, buttons) return open_dialogue("fb_id", user_id) buttons = [Button(title='Настройка', type='postback', payload='install'), Button(title='Другое', type='postback', payload='sup_other')] # Ask user to choose problem type msg = messages.type_support sub = get_info("sub", "fb_id", user_id) if sub != '-' and int(get_info("verified", "fb_id", user_id)): msg += f"\U000026A1 Ваша подписка: {sub}" fb_bot.send_button_message(user_id, msg, buttons)
def notify_clients(type_id, id, time_left, notified_clients): """ Check if time_left is 3 days or 1 day in seconds (minus 18 hours so we remind at 18:00 Beijing). Gap is 59 seconds because cycle repeats every 60 seconds, so it will not repeat on 1-60 'borders' """ if id == "0": return message = "" if 194341 <= time_left <= 194400: # 3 days left message = messages.left3days elif 21541 <= time_left <= 21600: # 1 day left message = messages.left1day elif -64859 <= time_left <= -64800: # Tariff expired message = messages.left_today if message: time.sleep(1) if type_id == "tg_id": buttons = types.InlineKeyboardMarkup() buttons.add(types.InlineKeyboardButton(text="\U0001F4B4 Оплата", callback_data="pay")) bot.send_message(int(id), message, reply_markup=buttons) elif type_id == "vk_id": keyboard = VkKeyboard(inline=True) keyboard.add_button("\U0001F4B4Оплата") vk_send_message(id, message, keyboard.get_keyboard()) elif type_id == "fb_id": fb_bot.send_text_message(int(id), message) update_clients([type_id, id], ['state', 'REMINDED']) notified_clients.add(get_info("email", type_id, id)) print(f"{datetime.datetime.today().date()} Notification sent to {type_id} {id}\n")
def tg_to_tg(to_id, message, from_support=False, review=False): """ Transfers messages from client to support and back Used instead of TG API forward_message method to be able to reply every client, even those who restricted forwarding """ text = message.text or message.caption or '' # Add info about client to message if not from_support: # Upper part of the message with emoji and name header = f"\U0001F4E2 Отзыв\n" if review \ else f"\U0001F4AC{message.from_user.first_name} {message.from_user.last_name}\n" # Bottom part of the message with id and social network name, so we can reply back check = "\U00002705" if get_info("verified", "tg_id", message.chat.id) else '' bottom = f"{message.chat.id} Telegram{check}" text = header + text + "\n\n" # Add client tariff info if not info_soon_check(message.chat.id, 'tg_id'): text += "\n" + client_info_msg("tg_id", message.chat.id) text += bottom if message.text: bot.send_message(to_id, text) time.sleep(1) if message.photo: photo_max_res = sorted(message.photo, key=lambda x: x.height)[-1].file_id bot.send_photo(to_id, photo_max_res, caption=text) time.sleep(1) if message.video: bot.send_video(to_id, message.video.file_id, caption=text) if message.document: bot.send_document(to_id, message.document.file_id, caption=text) if message.voice: bot.send_voice(to_id, message.voice.file_id) # Voice message has no caption, so send empty text message with top+bottom borders to be able to reply # Same for audio and stickers if not from_support: bot.send_message(to_id, text) if message.audio: bot.send_audio(to_id, message.audio.file_id) if not from_support: bot.send_message(to_id, text) if message.sticker: bot.send_sticker(to_id, message.sticker.file_id) if not from_support: bot.send_message(to_id, text)
def one_message_pass(message): """ User pushed the feedback button after previous support conversation was closed. Suppose user entering one-message review """ time.sleep(1) time_past = int(time.time()) - int( get_info("review_time", "tg_id", message.chat.id)) # If user pushed the button more than a day ago, don't send his message to support if time_past // 3600 >= 24: bot.send_message(message.chat.id, messages.buttons_menu) else: tg_to_tg(config.group_id, message, review=True) bot.send_message(message.chat.id, "Спасибо за отзыв!") update_clients(["tg_id", message.chat.id], ["state", "CLOSED"])
def buttons_handler(user_id, payload): """ Handles all available buttons """ time.sleep(1) if payload == "pay": open_dialogue("fb_id", user_id, state="PAY") buttons = [Button(title='Рубли ₽ / Гривны ₴', type='postback', payload='rub'), Button(title='Юани ¥', type='postback', payload='yuan'), Button(title='\U00002753Поддержка', type='postback', payload='sup')] fb_bot.send_button_message(user_id, messages.pay_type, buttons) elif payload == "trial": buttons = [Button(title='\U0001F4B4Оплата', type='postback', payload='pay'), Button(title='\U00002753Поддержка', type='postback', payload='sup')] fb_bot.send_button_message(user_id, messages.trial_text_vk, buttons) elif payload == "turk": buttons = [Button(title="Сайт обслуживания", type='web_url', url="http://tm.zgc.su"), Button(title="Как подключить?", type='web_url', url="https://sites.google.com/view/zgcvpn/try?authuser=0")] fb_bot.send_button_message(user_id, messages.turk, buttons) elif payload == "sup": support(user_id) elif payload == "urgent": support(user_id, urgent=True) elif payload == "other": buttons= [Button(title='\U0001F193Попробовать', type='postback', payload='trial'), Button(title='\U0001F1F9\U0001F1F2Туркменистан', type='postback', payload='turk'), Button(title='\U0001F6D2ZGC SHOP', type='web_url', url='https://market.zgc.su')] fb_bot.send_button_message(user_id, messages.buttons_menu, buttons) elif payload == "rub": fb_bot.send_text_message(user_id, messages.rub_text_vk) elif payload == "yuan": fb_bot.send_text_message(user_id, messages.yuan_text_vk) elif payload == "install": fb_bot.send_text_message(user_id, messages.first_install) elif payload == "sup_other": fb_bot.send_text_message(user_id, messages.support_vk) # If user rated quality less than 5 and pushed feedback button, open dialogue for one message only elif payload == "wish": fb_bot.send_text_message(user_id, messages.get_better) update_clients(["fb_id", user_id], ["state", "ONE MESSAGE"], ["review_time", f"{int(time.time())}"]) # Buttons to rate the quality of support elif payload in ["2", "4", "5"]: # User has already rated if get_info("rate", "fb_id", user_id) != "0": fb_bot.send_text_message(user_id, "Вы уже поставили оценку, спасибо!") return # Ask user to make review if he gave the highest rate if payload == "5": buttons = [Button(title="\U0001F49B Отзыв", type='web_url', url=config.review_link)] fb_bot.send_button_message(user_id, "Если вам понравился наш сервис - оставьте отзыв, " "и мы предоставим вам 10 дней бесплатного VPN!\n\n" "Когда оставите отзыв свяжитесь с нами для получения бонуса", buttons) # Ask user to write feedback else: buttons = [Button(title='\U0001F4A1 Пожелание', type='postback', payload='wish')] fb_bot.send_button_message(user_id, "Мы можем что-то улучшить в обслуживании?", buttons) bot.send_message(config.group_id, f"Клиент `{user_id}` поставил вам {payload}", parse_mode='Markdown') update_clients(["fb_id", user_id], ["rate", payload])
def buttons_handler(user_id, button_text): """ Handles all available buttons """ if button_text == "\U0001F4B4Оплата": open_dialogue("vk_id", user_id, state="PAY") keyboard = VkKeyboard(inline=True) keyboard.add_button("В рублях ₽ или в гривнах ₴") keyboard.add_line() keyboard.add_button("В юанях ¥") keyboard.add_line() keyboard.add_button("\U00002753Связаться с поддержкой") vk_send_message(user_id, messages.pay_type, keyboard.get_keyboard()) elif button_text == "В рублях ₽ или в гривнах ₴": vk_send_message(user_id, messages.rub_text_vk, reply_keyboard()) elif button_text == "В юанях ¥": vk_send_message(user_id, messages.yuan_text_vk, reply_keyboard()) elif button_text == "\U0001F193Попробовать": keyboard = VkKeyboard(inline=True) keyboard.add_button("\U0001F4B4Оплата") keyboard.add_line() keyboard.add_button("\U00002753Связаться с поддержкой") vk_send_message(user_id, messages.trial_text_vk, keyboard.get_keyboard()) elif button_text == "\U00002753Связаться с поддержкой": vk_support(user_id) elif button_text == "Срочная связь": vk_support(user_id, urgent=True) elif button_text == "Первичная настройка": vk_send_message(user_id, messages.first_install, reply_keyboard()) elif button_text == "Другое": vk_send_message(user_id, messages.support_vk, reply_keyboard()) elif button_text == "ZGC SHOP": open_dialogue("vk_id", user_id) vk_send_message( user_id, "Здравствуйте! Укажите, пожалуйста, продукт и вопросы по нему", reply_keyboard()) elif button_text == "\U0001F4F0Узнать больше": keyboard = VkKeyboard(inline=True) keyboard.add_openlink_button("Блог", "https://market.zgc.su/zgcvpnblog") vk_send_message( user_id, "Узнайте как заблокировать рекламу, какие появились сервера и многое другое", keyboard.get_keyboard()) elif button_text == "\U0001F1F9\U0001F1F2Туркменистан": keyboard = VkKeyboard(inline=True) keyboard.add_openlink_button("Сайт обслуживания", "http://tm.zgc.su") keyboard.add_line() keyboard.add_openlink_button( "Как подключить?", "https://sites.google.com/view/zgcvpn/try?authuser=0") vk_send_message(user_id, messages.turk, keyboard.get_keyboard()) elif button_text == "\U0001F6D2ZGC SHOP": keyboard = VkKeyboard(inline=True) keyboard.add_openlink_button("\U0001F6D2 ZGC SHOP", "https://market.zgc.su") keyboard.add_line() keyboard.add_button("Связаться с поддержкой") vk_send_message(user_id, messages.shop, keyboard.get_keyboard()) elif button_text == "Связаться с поддержкой": open_dialogue("vk_id", user_id) vk_send_message( user_id, "Здравствуйте! Укажите, пожалуйста, продукт и вопросы по нему", reply_keyboard()) elif button_text == "\U0001F91DСотрудничество": keyboard = VkKeyboard(inline=True) keyboard.add_openlink_button("Сделать предложение", "https://zgcvpn.ru/partnership") vk_send_message(user_id, messages.coop, keyboard.get_keyboard()) # If user rated quality less than 5 and pushed feedback button, open dialogue for one message only elif button_text == "\U0001F4A1 Оставить пожелание": vk_send_message(user_id, messages.get_better) update_clients(["vk_id", user_id], ["state", "ONE MESSAGE"], ["review_time", f"{int(time.time())}"]) # Buttons to rate the quality of support elif button_text in [ "\U0001F92C 1", "\U00002639 2", "\U0001F610 3", "\U0001F642 4", "\U0001F600 5" ]: keyboard = VkKeyboard(inline=True) # User has already rated if get_info("rate", "vk_id", user_id) != "0": vk_send_message(user_id, "Вы уже поставили оценку, спасибо!") return rating = button_text[-1] # Ask user to make review if he gave the highest rate if rating == "5": keyboard.add_openlink_button("\U0001F49B Оставить отзыв", config.review_link) vk_send_message( user_id, "Если вам понравился наш сервис - оставьте отзыв, " "и мы предоставим вам 10 дней бесплатного VPN!\n\n" "Когда оставите отзыв свяжитесь с нами для получения бонуса", keyboard=keyboard.get_keyboard()) # Ask user to write feedback else: keyboard.add_button("\U0001F4A1 Оставить пожелание") vk_send_message(user_id, "Мы можем что-то улучшить в обслуживании?", keyboard=keyboard.get_keyboard()) time.sleep(1) bot.send_message(config.group_id, f"Клиент `{user_id}` поставил вам {rating}", parse_mode='Markdown') update_clients(["vk_id", user_id], ["rate", rating])
def forward_vk_to_tg(event, review=False, user_state='OPEN'): """ Send client message to support with attachments and client tariff info """ time.sleep(1) user = vk.users.get(user_id=event.user_id) # Upper part of the message with emoji and name of the user top = "\U0001F4E2 Отзыв\n" if review else f"\U0001F4AC {user[0]['first_name']} {user[0]['last_name']}\n" # Bottom part of the message with id and social network name, so we can reply back check = "\U00002705" if get_info("verified", "vk_id", event.user_id) else '' bottom = f"{str(event.user_id)} Vkontakte{check}" attachments = vk.messages.getById( message_ids=event.message_id)['items'][0]['attachments'] if attachments: # Checker to avoid sending more than one caption caption_sent = False # Checker to make sure attachment successfully sent to support attachment_sent = False for att in get_attachments(event.message_id): if att.get('filter') == 'photo': # Send photo only with ID info caption, without message text if caption_sent: message = top + "\n" + bottom bot.send_photo(config.group_id, att.get('url'), caption=message) continue # Make sure we get string type anyway text = event.message or "" message = top + text + "\n\n" # Add client tariff info if not info_soon_check(event.user_id, 'vk_id'): message += client_info_msg("vk_id", event.user_id) message += bottom bot.send_photo(config.group_id, att.get('url'), caption=message) attachment_sent = True # Change this so we don't send the same caption with other photo caption_sent = True # Autoprocess payment using OCR if user_state == 'PAY': autopay(event.user_id, 'vk_id', att.get('url'), is_url=True) # notify support that user attached unsupported filetype if not attachment_sent: text = event.message or "" message = top + text + "\n_ОТ БОТА: клиент приложил в сообщение вконтакте файл, " \ "который нельзя отправить в телеграм_\n\n" if not info_soon_check(event.user_id, 'vk_id'): message += client_info_msg("vk_id", event.user_id) message += bottom bot.send_message(config.group_id, message, parse_mode='Markdown') else: message = top + event.message + "\n\n" # Add client tariff info if not info_soon_check(event.user_id, 'vk_id'): message += "\n" + client_info_msg("vk_id", event.user_id) message += bottom bot.send_message(config.group_id, message)
def react(call): """ Handles all callback buttons """ time.sleep(1) buttons = types.InlineKeyboardMarkup() user_info = db_find_value('tg_id', call.message.chat.id) if call.data == "rub": tariff_id = user_info['tariff'] msg = messages.rub_text # If user has active tariff, send button allowing to extend his current tariff # Temporarily off if False and tariff_id in ['1', '2', '3', '22', '23']: buttons.add( types.InlineKeyboardButton(text="Продлить текущий тариф", callback_data=f"Р-{tariff_id}")) msg += f"\n\nВаш текущий тариф: {tariff_id}" bot.send_message(call.message.chat.id, msg, parse_mode='Markdown', reply_markup=buttons) else: bot.send_message(call.message.chat.id, msg, parse_mode='Markdown') elif call.data == "yuan": bot.send_message(call.message.chat.id, messages.yuan_text, parse_mode='Markdown') elif call.data == "install": bot.send_message(call.message.chat.id, messages.first_install) elif call.data == "other": bot.send_message(call.message.chat.id, messages.support, parse_mode='Markdown') elif call.data == "market": open_dialogue("tg_id", call.message.chat.id) bot.send_message( call.message.chat.id, 'Здравствуйте! Укажите, пожалуйста, продукт и вопросы по нему') elif call.data == "urgent": support(call.message, urgent=True) elif call.data == "sup": support(call.message) # If user rated quality less than 5 and pushed feedback button, open dialogue for one message only elif call.data == "get_better": update_clients(["tg_id", call.message.chat.id], ["state", "ONE MESSAGE"], ["review_time", f"{int(time.time())}"]) bot.send_message(call.message.chat.id, messages.get_better) elif call.data == "pay": buttons.add( types.InlineKeyboardButton(text="В рублях или в гривнах", callback_data="rub")) buttons.add( types.InlineKeyboardButton(text="В юанях", callback_data="yuan")) buttons.add( types.InlineKeyboardButton( text="\U00002753 Связаться с поддержкой", callback_data="sup")) bot.send_message(call.message.chat.id, messages.pay_type, reply_markup=buttons) elif call.data in ["1", "2", "3", "4", "5"]: # User has already rated if get_info("rate", "tg_id", call.message.chat.id) != "0": bot.send_message(call.message.chat.id, "Вы уже поставили оценку, спасибо!") return rating = call.data # Ask user to make review if he gave the highest rate if rating == "5": buttons.add( types.InlineKeyboardButton(text="\U0001F49B Оставить отзыв", url=config.review_link)) bot.send_message( call.message.chat.id, "Если вам понравился наш сервис - оставьте отзыв, " "и мы предоставим вам 10 дней бесплатного VPN!\n\n" "_Когда оставите отзыв свяжитесь с нами для получения бонуса_", reply_markup=buttons, parse_mode='Markdown') # Ask user to write feedback else: buttons.add( types.InlineKeyboardButton( text="\U0001F4A1 Оставить пожелание", callback_data="get_better")) bot.send_message(call.message.chat.id, "Мы можем что-то улучшить в обслуживании?", reply_markup=buttons) bot.send_message( config.group_id, f"Клиент `{call.message.chat.id}` поставил вам {rating}", parse_mode='Markdown') update_clients(["tg_id", call.message.chat.id], ["rate", rating]) # User chose tariff he wants to buy, send him buttons to choose tariff duration in days (ascending order) # Temporarily off elif False and re.fullmatch(r'[РЮ]-[123]+', call.data): currency, tariff_id = call.data.split('-') tariffs_list = get_tariffs(['tariff', tariff_id], ['currency', currency]) for price, tariff_id, days, currency, desc in sorted( tariffs_list, key=lambda x: int(x[2])): # Telegram does not support prices lower than 60 rub. if int(price) < 60: continue # Choose correct form of 'день' word if days[-1] == '1': day_word = 'день' elif days[-1] in '234': day_word = 'дня' else: day_word = 'дней' buttons.add( types.InlineKeyboardButton( text=f"{days} {day_word}", callback_data=f"{currency}-{tariff_id}-{days}")) bot.send_message( call.message.chat.id, f"Выберите, на сколько дней вы хотите продлить тариф {tariff_id}", reply_markup=buttons) # User chose tariff duration in days, send him payment button # Temporarily off elif False and re.fullmatch(r'[РЮ]-[123]+-[0-9]+', call.data): currency, tariff_id, days = call.data.split('-') specific_tariff = get_tariffs(['tariff', tariff_id], ['currency', currency], ['days', days])[0] price = int(specific_tariff[0]) prices = [ types.LabeledPrice(label=f'Тариф {tariff_id} на {days} дней', amount=price * 100) ] bot.send_invoice(call.message.chat.id, title=f"Тариф {tariff_id}", description='Plati', provider_token=config.pay_token, currency='rub', prices=prices, start_parameter='payment', invoice_payload='HAPPY FRIDAYS COUPON')
elif message.text and message.text.lower().split()[0] == "/закрыть": if len(message.text.lower().split()) != 2: bot.send_message( message.chat.id, "Отправьте команду и айди через пробел, например:\n" "/закрыть 1234567") return for id_type in ["tg_id", "vk_id", "fb_id"]: close_dialogue(id_type, message.text.lower().split()[1], silent=True) @bot.message_handler(func=lambda message: get_info( "state", "tg_id", message.chat.id) in ["OPEN", "REMINDED", "PAY"], content_types=[ 'text', 'photo', 'video', 'voice', 'audio', 'sticker', 'document' ]) def forward_to_support(message): """ Forward all clients messages to support group if dialogue is open Also send to support info about client's tariff """ time.sleep(1) user_info = db_find_value("tg_id", message.chat.id) user_state = user_info['state'] # transfer client's message to support group tg_to_tg(config.group_id, message)