Hierarchical message_handler Telegram bot - bots

may be someone could advice me with the issue
Recently started studying Python. Now want to make my first Telegram bot. The project idea is that first command is the name and second is the day to show schedule. The problem is that code works fine for first name entry but does not work properly with second name entry: strange is if I start with "helen" and "monday" and funcs works properly, then I put command "Alice" and func(alice) also works fine but then then result is getting from funcs which are below func(helen). Do not see logic in this
Tried a lot, may be here should be totally different approach?
Desired: input /name, input /day --. receive extrect from dict by name and date
Result: when input new name code does not react to it
import telebot
token = "xxxx"
bot = telebot.TeleBot(token)
girls = {
"Alice": {
"Monday" : {"School: 8-15", "Art: 19-20"},
"Tuesday" : {"School: 8-15", "Music: 17-18"},
},
"Helen": {
"Monday": {"School: 8-13", "Music: 16-16", "Art: 19-20"},
"Tuesday": {"School : 8-13", "Chess: 14-15", "Sport: 17-18"},
}
}
#bot.message_handler(commands=['alice', "Alice"])
def alice (message):
#bot.message_handler(commands=['monday', "Monday"])
def monday (message):
bot.send_message(message.chat.id, 'Alice at Monday:')
for value in girls["Alice"]["Monday"]:
bot.send_message(message.chat.id, value)
#bot.message_handler(commands=['tuesday', "Tuesday"])
def tuesday (message):
bot.send_message(message.chat.id, 'Alice at Tuesday:')
for value in girls["Alice"]["Tuesday"]:
bot.send_message(message.chat.id, value)
#bot.message_handler(commands=['helen', "Helen"])
def helen (message):
#bot.message_handler(commands=['monday', "Monday"])
def monday (message):
bot.send_message(message.chat.id, 'Helen at Monday')
for value in girls["Helen"]["Monday"]:
bot.send_message(message.chat.id, value)
#bot.message_handler(commands=['tuesday', "Tuesday"])
def tuesday (message_1):
bot.send_message(message_1.chat.id, 'Helen at Monday')
for value in girls["Helen"]["Tuesday"]:
bot.send_message(message_1.chat.id, value)
bot.polling(none_stop=True)

Related

Python Discord Bot - VPS Reboot Behaviors?

With a friend of mine we created a simple discord bot in python, first let me explain how it works :
We created 366 different usernames, one for each day of the year. Each day at 0:01AM the bot should automatically post a message with :
Current date (day, month, year) and the username we associated with it
The bot should also rename its own username with the username of the day
Here is the code we made :
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import discord
from discord.ext import commands
from dotenv import load_dotenv
from datetime import datetime
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
client = discord.Client()
channel = client.get_channel(CHANNELID)
bissextileSpec = datetime.today().strftime('%m-%d') # To handle bissextile years
nickFile = open("nicknames.txt", "r")
nickList = nickFile.readlines()
dayNumber = datetime.now().timetuple().tm_yday
# We also made special dates
if bissextileSpec == '06-01' :
nickOfTheDay = 'SpecialNick1'
elif bissextileSpec == '07-14' :
nickOfTheDay = 'SpecialNick2'
elif bissextileSpec == '30-12' :
nickOfTheDay = 'SpecialNick3'
elif bissextileSpec == '17-06' :
nickOfTheDay = 'SpecialNick4'
elif bissextileSpec == '05-04' :
nickOfTheDay = 'SpecialNick5'
else :
nickOfTheDay = nickList[dayNumber - 1]
await channel.send('MSG CONTENT', nickOfTheDay, 'MSG CONTENT')
await client.user.edit(username=nickOfTheDay)
We know our way a bit around python but we don't really know how discord bots works :
We are not quite sure how to instruct it to auto-post at midnight each day : We thought of a While loop with a sleep(50) on its end BUT :
How is it going to handle the hazardous VPS reboots ? If the vps reboot mid sleep is it going to reset it and shift the next post time further than 0:00 ?
On the other end, if we don't use a While loop, but if we use the CRON system in Linux to check and start the script everyday at midnight, does it mean the bot will be shown offline 23h59/24 on Discord and stay online just to post the message ? => We want to add a few more features later so we need the bot to run 24/24
Aswell, do not hesitate to point it if we did something wrong in the code ( ͡° ͜ʖ ͡°)
You can make a loop that iterates every 24h and change the nickname of the bot, you can get the seconds till midnight with some simple math and sleep for those seconds
import asyncio
from discord.ext import tasks
from datetime import datetime
#tasks.loop(hours=24)
async def change_nickname(guild):
"""Loops every 24 hours and changes the bots nick"""
nick = "" # Get the nick of the corresponding day
await guild.me.edit(nick=nick)
#change_nickname.before_loop
async def before_change_nickname(guild):
"""Delays the `change_nickname` loop to start at 00:00"""
hour, minute = 0, 0
now = datetime.now()
future = datetime(now.year, now.month, now.day + 1, now.month, now.day, hour, minute)
delta = (future - now).seconds
await asyncio.sleep(delta)
To start it you need to pass a discord.Guild instance (the main guild where the nickname should be changed)
change_nickname.start(guild) # You can start it in the `on_ready` event or some command or in the global scope, don't forget to pass the guild instance
No matter what hour the bot started the loop will change the bots nick at 00:00 everyday
Reference:
tasks.loop
Loop.before_loop
Loop.start
Łukasz's code has a tiny flaw, the future variable is wrongly initialized but everything else is working accordingly! This should do the trick:
import asyncio
from discord.ext import tasks
from datetime import datetime
#tasks.loop(hours=24)
async def change_nickname(guild):
nick = ""
await guild.me.edit(nick=nick)
#change_nickname.before_loop
async def before_change_nickname():
hour, minute = 0, 0
now = datetime.now()
future = datetime(now.year, now.month, now.day + 1, hour, minute)
delta = (future - now).seconds
await asyncio.sleep(delta)

How do I make a snipe command that shows the timestamp in the footer

I am able to get the time in UTC using the 'pytz' library and 'datetime' library, but I need it in local time of the user. Say you run the snipe command from the USA, you should get your local time, and if I run it from say Italy, I should get Italy's time. I hope I made it clear.
x = message = {}
y = author = {}
z = author_avatar = {}
time = {}
#client.event
async def on_message_delete(msg):
UTC = pytz.utc
datetime_utc = datetime.now(UTC)
if msg.author.bot == False:
x[msg.channel.id] = msg.content
y[msg.channel.id] = msg.author
time[msg.channel.id] = datetime_utc.strftime('%H:%M UTC')
if msg.author == client.user:
x[msg.channel.id] = msg.content
y[msg.channel.id] = msg.author
time[msg.channel.id] = datetime_utc.strftime('%H:%M UTC')
#client.command(name = 'snipe')
async def snipe(ctx):
try:
em = discord.Embed(description = f" {x[ctx.channel.id]}" ,color = random.choice(colors_for_embeds1), timestamp = datetime.now())
em.set_author(name = y[ctx.channel.id] ,icon_url = (y[ctx.channel.id]).author.url)
em.set_footer(text = f"at {time[ctx.channel.id]}")
await ctx.send(embed = em)
except:
await ctx.send("There is nothing to snipe!")
This is how the command works. The deleted message gets added to a dictionary with the channel ID as the key, the author id gets saved in a dictionary with the channel ID.
I hope this answers your question.
UTC time updates for your location, so for you, it would show your time (example: Today at 8:00 AM) then for someone else that is somewhere else in the world would show (Today at 9:00 AM).
I don't know if I answered this well or not, or if you understood it.
But hope answers your question! :D
your bot has no way of knowing the timezone of the people running the command. The timestamp on discord embeds always show the time in the local format for the people who see the embed, so different people will see different times depending on their timezones.
A solution would be to record the user timezone with a different command and save it to a database.
Then on your command parse the time into the footer for the timezone you want.

Odoo 12 : How to prevent default field method to be executed

I scheduled an cron that execute every 1st of the month, the purpose is to allocate leave for all employee according to their tags. here is sample of my code :
for leave in leave_type_ids:
for employee_tag in employee_tag_ids:
values = {
'name': 'Allocation mensuelle %s %s' % (now.strftime('%B'), now.strftime('%Y')),
'holiday_status_id': leave.id,
'number_of_days': employee_tag.allocation,
'holiday_type': 'category',
'category_id': employee_tag.id,
}
try:
self.create(values).action_approve()
except Exception as e:
_logger.critical(e)
I want to point out that self is instance of 'hr.leave.allocation'.
The problem is when I create the record, the field employee_id is automatically fill with the user/employee OdooBot (the one who executed the program in the cron) and that is not all, the employee OdooBot was allocated a leaves.
This behavior is due to those codes in odoo native modules :
def _default_employee(self):
return self.env.context.get('default_employee_id') or self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
employee_id = fields.Many2one(
'hr.employee', string='Employee', index=True, readonly=True,
states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, track_visibility='onchange')
So my question is how to prevent this when it's the cron and set it to normal when it's in Form view?
The field "employé" should be empty here (in image below), because it is an allocation by tag.
You must loop over hr.employee because then you can do either of the following:
self.with_context({'default_employee_id': employee.id}).create(...)
OR
self.sudo(employee.id).create(...)

Telegram Bot profile with calendar

I am writing a bot profile. After the answer, the following question should be asked. In one of the steps you need to select a date. I use a ready-made calendar. But after choosing a date, the next question is not asked, but the script does not crash, only waits for input and throws the Exception defined in the code below. How to fix that would bring up the next question "What is your name?". Thank. Here is the part of the code that includes the previous calendar question and the next question:
def process_Personenanzahl_step(message):
try:
chat_id = message.chat.id
user = user_dict[chat_id]
user.carModel = message.text
markup = types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True)
itembtn1 = types.KeyboardButton('1')
itembtn2 = types.KeyboardButton('2')
itembtn3 = types.KeyboardButton('3')
itembtn4 = types.KeyboardButton('4')
itembtn5 = types.KeyboardButton('5')
markup.add(itembtn1, itembtn2, itembtn3, itembtn4, itembtn5)
msg = bot.send_message(chat_id, 'Qun', reply_markup=markup)
bot.register_next_step_handler(msg, check_calendar_messages)
except Exception as e:
bot.reply_to(message, 'ooops!!')
calendar_1 = CallbackData("calendar_1", "action", "year", "month", "day")
def check_calendar_messages(message):
now = datetime.datetime.now() # Get the current date
bot.send_message(
message.chat.id,
"Selected date",
reply_markup=telebot_calendar.create_calendar(
name=calendar_1.prefix,
year=now.year,
month=now.month, # Specify the NAME of your calendar
),
)
#bot.callback_query_handler(func=lambda call: call.data.startswith(calendar_1.prefix))
def callback_inline(call: object) -> object:
# At this point, we are sure that this calendar is ours. So we cut the line by the separator of our calendar
name, action, year, month, day = call.data.split(calendar_1.sep)
# Processing the calendar. Get either the date or None if the buttons are of a different type
date = telebot_calendar.calendar_query_handler(
bot=bot, call=call, name=name, action=action, year=year, month=month, day=day
)
# There are additional steps. Let's say if the date DAY is selected, you can execute your code. I sent a message.
if action == "DAY":
msg = bot.send_message(
chat_id=call.from_user.id,
text=f"You have chosen {date.strftime('%d.%m.%Y')}",
reply_markup=ReplyKeyboardRemove(),
)
print(f"{calendar_1}: Day: {date.strftime('%d.%m.%Y')}")
bot.register_next_step_handler(msg, process_fullname_step)
def process_fullname_step(message):
try:
chat_id = message.chat.id
user_dict[chat_id] = User(message.text)
markup = types.ReplyKeyboardRemove(selective=False)
msg = bot.send_message(chat_id, 'What is your name?', reply_markup=markup)
bot.register_next_step_handler(msg, process_fullname_step)
except Exception as e:
bot.reply_to(message, 'ooops!!')

Cron error old_api while running Schedule Action odoo 8

I have created a function which is working fine if i use it through the view. but it's not working on schedule action, in openerp log show the following error.
TypeError: old_api() takes at least 4 arguments (3 given)
My module
class account_invoice(models.Model):
_name = 'account.invoice'
_rec_name = 'invoice_number'
#api.multi
def create_invoice(self):
id = self.id
amount = 0
journal = self.env['journal.entry']
for credit in self.invoice_line:
fee = credit.amount * credit.qty
if credit.account.parent.type.name == "Revenue":
journal.sudo().create({'account': credit.account.id,
'credit': fee,
'student_id' : self.student_id.id})
For method to work as a scheduled action you should call it with #api.model decorator instead of #api.multi, which works with view buttons.

Resources