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)
Related
import pymsteams
import win32com.client as client
from datetime import datetime, timedelta
import pywintypes
import time
def outlookFind():
outlook = client.Dispatch("Outlook.Application")
namespace=outlook.GetNamespace("MAPI")
inbox = namespace.GetDefaultfolder(6)
messages = inbox.Items
received_datime = datetime.now()- timedelta(seconds=30)
# print(received_datime)
received_datime = received_datime.strftime('%m/%d/%Y %H:%M:%S %p')
# print(received_datime)
messages = messages.Restrict("[ReceivedTime] >= '" + received_datime + "'")
messages = messages.Restrict("[SenderEmailAddress] = 'contact#mail'")
messages = messages.Restrict("[Subject] = 'Long Running WEBService!'")
for message in messages:
if message:
myTeamsMessage = pymsteams.connectorcard("webhook for teams channel")
myTeamsMessage.text("You got a new mail with subject name Long Running WEBService!")
myTeamsMessage.send()
if __name__=="__main__" :
while True:
outlookFind()
This is the code i wrote for post notifications in teams when i got a mail in outlook. when i run this its post continuous notifications until th next minut starts. i want to post one notification for one mail.
i run this code in background using pythonw.exe
Hi guys i am very new to python and i would very much appreciate some help on this matter, i have been trying to get the actual time with each record i send to the database but i think because it is a loop it seems to record the beginning time and loop it.
could anybody help me out with this?
HOW CAN I GET REAL TIME INSTEAD OF THE SAME TIME GETTING LOOPED
# Author: Aswin Ramamchandran
# Version: 1.1
from time import sleep
import datetime
import pymongo
import time
# This URL provides connection to the database
uri = blahblah
# initialising pymongo client
client = pymongo.MongoClient(uri)
# Database where the records will be saved - reference to the database
db = client.Kostenanalyse
# Accessing the collection "latenz" from the Database
coll = db.latenz
#Defining the Start time
start_time = datetime.datetime.now()
start_time = start_time.isoformat()
end = time.perf_counter()
def create_latenz_data()-> dict:
return {
"Temperature" : "",
"Time when packet was sent" : start_time,
"Sensor A reading" : "",
"Latency" : end,
}
#While loop
while True:
data = create_latenz_data()
start = time.perf_counter()
coll.insert_one(data)
end = time.perf_counter() - start
print('{:.6f}s for the calculation'.format(end))
print(str(start_time) + 'Wrote data sample {} to collpipection {}'.format(data, 'latenz'))
sleep(0.5)
Your script stores the start_time variable at load and does not change it. As you have used that same variable inside while loop and inside create_latenz_data(), replacing start_time with datetime.datetime.now().isoformat() directly so new time is picked everytime it is called.
from time import sleep
import datetime
import pymongo
import time
# This URL provides connection to the database
uri = blahblah
# initialising pymongo client
client = pymongo.MongoClient(uri)
# Database where the records will be saved - reference to the database
db = client.Kostenanalyse
# Accessing the collection "latenz" from the Database
coll = db.latenz
def create_latenz_data()-> dict:
return {
"Temperature" : "",
"Time when packet was sent" : datetime.datetime.now().isoformat(),
"Sensor A reading" : "",
"Latency" : end,
}
#While loop
while True:
data = create_latenz_data()
start = time.perf_counter()
coll.insert_one(data)
end = time.perf_counter() - start
print('{:.6f}s for the calculation'.format(end))
print(str(datetime.datetime.now().isoformat()) + 'Wrote data sample {} to collpipection {}'.format(data, 'latenz'))
sleep(0.5)
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.
I set up a periodic task using celery beat. The task runs and I can see the result in the console.
I want to have a python script that recollects the results thrown by the tasks.
I could do it like this:
#client.py
from cfg_celery import app
task_id = '337fef7e-68a6-47b3-a16f-1015be50b0bc'
try:
x = app.AsyncResult(id)
print(x.get())
except:
print('some error')
Anyway, as you can see, for this test I had to copy the task_id thrown at the celery beat console (so to say) and hardcode it in my script. Obviously this is not going to work in real production.
I hacked it setting the task_id on the celery config file:
#cfg_celery.py
app = Celery('celery_config',
broker='redis://localhost:6379/0',
include=['taskos'],
backend = 'redis'
)
app.conf.beat_schedule = {
'something': {
'task': 'tasks.add',
'schedule': 10.0,
'args': (16, 54),
'options' : {'task_id':"my_custom_id"},
}
}
This way I can read it like this:
#client.py
from cfg_celery import app
task_id = 'my_custom_id'
try:
x = app.AsyncResult(id)
print(x.get())
except:
print('some error')
The problem with this approach is that I lose the previous results (previous to the call of client.py).
Is there some way I can read a list of the task_id's in the celery backend?
If I have more than one periodic tasks, can I get a list of task_id's from each periodic task?
Can I use app.tasks.key() to accomplish this, how?
pd: not english-speaking-native, plus new to celery, be nice if I used some terminology wrong.
OK. I am not sure if nobody answered this because is difficult or because my question is too dumb.
Anyway, what I wanted to do is to get the results of my 'celery-beat' tasks from another python process.
Being in the same process there was no problem I could access the task id and everything was easy from there on. But from other process I didn't find a way to retrieve a list of the finished tasks.
I tried python-RQ (it is nice) but when I saw that using RQ I couldn't do that either I came to understand that I had to manually make use of redis storage capabilities. So I got what I wanted, doing this:
. Use 'bind=True' to be able to instrospect from within the task function.
. Once I have the result of the function, I write it in a list in redis (I made some trick to limit the sizeof this list)
. Now I can from an independent process connect to the same redis server and retrieve the results stored in such list.
My files ended up being like this:
cfg_celery.py : here I define the way the tasks are going to be called.
#cfg_celery.py
from celery import Celery
appo = Celery('celery_config',
broker='redis://localhost:6379/0',
include=['taskos'],
backend = 'redis'
)
'''
urlea se decoro como periodic_task. no hay necesidad de darla de alta aqi.
pero como add necesita args, la doy de alta manualmente p pasarselos
'''
appo.conf.beat_schedule = {
'q_loco': {
'task': 'taskos.add',
'schedule': 10.0,
'args': (16, 54),
# 'options' : {'task_id':"lcura"},
}
}
taskos.py : these are the tasks.
#taskos.py
from cfg_celery import appo
from celery.decorators import periodic_task
from redis import Redis
from datetime import timedelta
import requests, time
rds = Redis()
#appo.task(bind=True)
def add(self,a, b):
#result of operation. very dummy.
result = a + b
#storing in redis
r= (self.request.id,time.time(),result)
rds.lpush('my_results',r)
# for this test i want to have at most 5 results stored in redis
long = rds.llen('my_results')
while long > 5:
x = rds.rpop('my_results')
print('popping out',x)
long = rds.llen('my_results')
time.sleep(1)
return a + b
#periodic_task(run_every=20)
def urlea(url='https://www.fullstackpython.com/'):
inicio = time.time()
R = dict()
try:
resp = requests.get(url)
R['vato'] = url+" = " + str(resp.status_code*10)
R['num palabras'] = len(resp.text.split())
except:
R['vato'] = None
R['num palabras'] = 0
print('u {} : {}'.format(url,time.time()-inicio))
time.sleep(0.8) # truco pq se vea mas claramente la dif.
return R
consumer.py : the independent process that can get the results.
#consumer.py
from redis import Redis
nombre_lista = 'my_results'
rds = Redis()
tamaño = rds.llen(nombre_lista)
ultimos_resultados = list()
for i in range(tamaño):
ultimos_resultados.append(rds.rpop(nombre_lista))
print(ultimos_resultados)
I am relatively new to programming and I hope that this answer can help noobs like me. If I got something wrong feel free to make the corrections as necessary.
In my code I'm trying to request ticket data from zendesk via zenpy wrapper. I've got a script that pulls all the data I want, but for some reason it's ignoring the part regarding the date. Any idea what I'm doing wrong?
from datetime import datetime, timedelta
creds = {
'email' : 'login',
'password' : 'info',
'subdomain': 'domain'
}
yesterday = datetime.now() - timedelta(hours=1)
today = datetime.now()
from zenpy import Zenpy
zenpy = Zenpy(**creds)
for ticket in zenpy.search("test", type="ticket", created_greater_than=(yesterday)):
print(ticket.id)
id = ticket.id
subj = ticket.subject
created = ticket.created_at
for comment in zenpy.tickets.comments(ticket.id):
body = comment.body
While it may not fix your problem, using variable names of yesterday and today when your timedelta is 1 hour is extremely misleading. You probably want
yesterday = datetime.now() - timedelta(days=1)
today = datetime.now()
instead of
yesterday = datetime.now() - timedelta(hours=1)
today = datetime.now()
This also has a subtle fail run if run at the exact moment the clock rolls over to a new day which can be avoided by setting today first
today = datetime.now()
yesterday = today - timedelta(days=1)
today = datetime.now()
yesterday = today - timedelta(days=1)
for ticket in Z.search(type='ticket', created_between=[yesterday, today]):
...
This worked for me last time I used zenpy.
Make sure zenpy is updated as zendesk has been known to add/change endpoints. So if created_greater_than is an accepted argument in the zendesk api than it's possible that zenpy hasn't added it, or you're not on the latest version.