Calculate Time Difference Between Two Discord IDs/Snowflakes - python

The command timedif takes two message IDs and calculates the difference in time that they were sent, accurate to 2 decimal places.
This is what I have made:
#bot.command(name='timedif', help='', aliases=['snowflake', 'timediff'])
async def timedif(ctx, id1, id2):
try:
id1 = int(id1)
id2 = int(id2)
except:
await ctx.reply("Check your message ID's! They are incorrect!")
msg1 = await ctx.fetch_message(id1)
msg2 = await ctx.fetch_message(id2)
time1 = msg1.created_at
time2 = msg2.created_at
ts_diff = time2 - time1
secs = abs(ts_diff.total_seconds())
days,secs=divmod(secs,secs_per_day:=60*60*24)
hrs,secs=divmod(secs,secs_per_hr:=60*60)
mins,secs=divmod(secs,secs_per_min:=60)
secs=round(secs, 2)
answer='{} secs'.format(secs)
if secs > 60:
answer='{} mins and {} secs'.format(int(mins),secs)
if mins > 60:
answer='{} hrs, {} mins and {} secs'.format(int(hrs),int(mins),secs)
if hrs > 24:
answer='{} days, {} hrs, {} mins and {} secs'.format(int(days),int(hrs),int(mins),secs)
embed = discord.Embed(title="**Time Difference**", description=f"""IDs: {id1}, {id2}
Time difference between the 2 IDs:
{answer}""")
await ctx.reply(embed=embed)
I tested the bot but I encountered a problem:
For this code, it can only fetch messages from the same channel as ‘ctx’. But if I am taking one of the IDs from another channel (ie. a DM channel), the bot cannot access it. Is there an algorithm/function that allows me to calculate the time difference of any 2 ID’s? I think it’s possible as a lot of bots have been doing it recently.
To clarify: for messages from ctx.channel, it works fine and is able to calculate the time diff. The only problem lies in it not able to fetch messages from other channels.

Calculating the time difference between any two Discord IDs doesn't require any API requests. Since the creation time of every snowflake object is encoded within that 19-21 digits. When read in binary, bit 22 and up is the timestamp.
For recent discord.py versions, there's already a helper method for you!
time1 = discord.utils.snowflake_time(int(id1))
time2 = discord.utils.snowflake_time(int(id2))
ts_diff = time2 - time1
secs = abs(ts_diff.total_seconds())
If this doesn't exist for your version yet, it's simple to implement snowflake_time():
import datetime
def snowflake_time(id):
return datetime.datetime.utcfromtimestamp(((id >> 22) + 1420070400000) / 1000)
This method works for any Discord ID (message ID, channel ID, guild ID, category ID, audit log ID, etc)
Discord Snowflake Structure: https://discord.com/developers/docs/reference#snowflakes.

Related

Datetime - figuring out how long until a specified time

discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'datetime.timedelta' object has no attribute 'hours'
previous = datetime.datetime.utcnow() - datetime.timedelta(hours=24)
if not find_user:
await self.client.boosts.insert_one(
{"userID": ctx.author.id, "boost_type": random_boost_type,
"boost_amount": round(random_boost_value, 2),
"boost_time_given": datetime.datetime.utcnow()})
elif find_user:
if find_user["boost_time_given"] > previous:
embed = await embedHelper.error_embed(self.client, "You have already ran this command in the last 24 hours!")
embed.description = f"`You must wait {find_user['boost_time_given'] - datetime.timedelta(hours=24).hours}`"
return await ctx.channel.send(embed=embed)
Hey guys. Currently trying to make the bottom embed send and display how many hours left until find_user["boost_time_given"] < previous
However, datetime.timedelta has no attribute hours?
Don't suppose anybody knows how I can display the hours left?
AS find_user['boost_time_given'] is a datetime, you don't want to substract a timedelta from it, but rather get the difference of it and previous. Then use total_seconds() to get an hour duration
embed.description = f"`You must wait {(find_user['boost_time_given'] - previous).total_seconds()//3600}`"
But to get a nice display will all units just do
embed.description = f"`You must wait {find_user['boost_time_given'] - previous}`"

Discord py message.createdat

im wondering how i can format the following:
async def sendmessage():
channel = client.get_channel(786258218925293629)
fetchMessages = await channel.history().find(lambda m: m.author.id == 627862713242222632)
print(fetched.created_at)
my end goal is to check if the message was sent within the last 30 minutes, and if it was, delete the message.
I need to format it into how many seconds ago the message was sent. Currently the output is:
2020-12-13 20:19:24.414000
Any help would be greatly appriciated.
You can get how many seconds ago the message was sent by subtracting the created_at datetime object from the datetime object that represents the current time. The code to do this should look something like this:
import datetime # put this at the top of your code
...
async def sendmessage():
channel = client.get_channel(786258218925293629)
fetchMessages = await channel.history().find(lambda m: m.author.id == 627862713242222632)
# i'm not sure where the fetched variable is from so i'm assuming you just forgot to include that bit of code
msg_secs = (datetime.datetime.now() - fetched.created_at).total_seconds()
print(int(msg_secs))
API References:
datetime / timedelta object for datetime subtraction
created_at property of message

Python: fetching live chat but too slow

I had to fetch live chat on Youtube and chose to use pytchat instead of Youtube API. There's no problem to fetch the chat, but it was a bit slow.
from pytchat import LiveChat
from datetime import datetime
chat = LiveChat(video_id = "36YnV9STBqc")
k=[]
while chat.is_alive():
now2 = datetime.now()
current_time2 = now2.strftime("%H:%M:%S")
print("Current Time =", current_time2,"==========") #A
try:
data = chat.get()
now3 = datetime.now()
current_time3 = now3.strftime("%H:%M:%S")
print("Current Time =", current_time3,"~~~~~~~~~~~~~") #B
for c in data.items:
comment=f"[{c.datetime}-{c.message}]"
k.append(comment) #I need to use k later
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print(comment,"Current Time =", current_time,"++++++++++++++++")
data.tick()
except KeyboardInterrupt:
chat.terminate()
break
Below shows the output of a video with 17,649 watching: (B to A took 5 secs)
Current Time = 18:49:33 ========== #A
Current Time = 18:49:33 ~~~~~~~~~~~~~ #B
[2020-05-30 18:49:29-hi] Current Time = 18:49:33 ++++++++++++++++ #4 seconds late
[2020-05-30 18:49:32-how are you] Current Time = 18:49:36 ++++++++++++++++
Current Time = 18:49:38 ========== #A
Current Time = 18:49:38 ~~~~~~~~~~~~~ #B
[2020-05-30 18:49:32-so good] Current Time = 18:49:38 ++++++++++++++++ #6 seconds late
Below shows the output of a video with 702 watching: (B to A took at least 10 secs)
Current Time = 18:49:09 ========== #A
Current Time = 18:49:10 ~~~~~~~~~~~~~ #B
[2020-05-30 18:49:06-hellp] Current Time = 18:49:10 ++++++++++++++++
[2020-05-30 18:49:07-love the music] Current Time = 18:49:15 ++++++++++++++++
Current Time = 18:49:20 ========== #A
Current Time = 18:49:20 ~~~~~~~~~~~~~ #B
[2020-05-30 18:49:15-???] Current Time = 18:49:20 ++++++++++++++++
I assume that different watching amounts will effect the time? It's also 4 to 6 secs late to fetch every chat, is it possible to solve it? Or it's just how Pytchat works?
This is a specification.
Pytchat gets the chat in exactly the same way as the browser.
If your browser displays the time of the chat and the current time down to the second, you'll get the same results.
The response of the YouTube server is presumably affected by the number of people watching the chat and the number of people posting the chat at any given time.
It needs to be verified, but as you pointed out, I'm guessing that if a lot of chat posts are made, it's taking longer for the YouTube server to process them and return the chats that are retrieved.
(If you comment out the data.tick(), you might get a little better results.)
Use
while chat.is_alive():
data = chat.get()
for c in data.sync_items():
#c can then be formatted for ur stuff
print("Formatting stuff")
sync_items() will give you appropriate realtime chat movement.

Function with a Cooldown

Hello i have a Person Detector script i want to send an info if any person detected by mail.In order to prevent mail spamming i need a timer for sendMail function.Function might be triggered anytime but it will only respond if its not on cooldown.
I tried using async task but couldn't implemented because if a person detected it goes to a loop where it sends email every 5 minutes even if there isn’t any person detected after the first sight.
Example:
Person detection script is running.
Person detected on camera -> Send an email(start the 5 minute cooldown)
Person sighted again after 2 minutes(didn't send any email because there is still 3 minutes cooldown).
Person sighted after 6 minutes send another email(because 5 minute cooldown is over).
Summary of my code.(Necessary parts only detection and sending mail works cooldown (timer) doesn't work
async def sendAlert():
server.sendmail(sender_email, receiver_email, message)
print('sent!')
await asyncio.sleep(300)
if __name__ == "__main__":
while True:
for i in range(len(boxes)):
if classes[i] == 1 and scores[i] > threshold:
with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
sendAlert(server)
box = boxes[i]
cv2.rectangle(img,(box[1],box[0]),(box[3],box[2]),(255,0,0),2)
If there is a person detected, script will send an alert by email.Afterwards if a person detected again in 5 minutes sendAlert function shouldn't respond until 5 minutes passed
I agree with #Prune that you need to create a small (minimal) use-case and present your code so that it is not only relevant to you, but also relevant to others. Additionally, your question should have a section with a verifiable example. Without these attributes, your question becomes hard for people to grasp, solve and/or suggest any verifiable solution.
However, as I understand, you have some action (sending email if a person is detected) that you would like to perform after certain cool-off period. So, in other words, you want a mechanism of keeping track of time. Hence, you would need the datetime library.
So, your pseudo code should look something like this:
Pseudo Code
import datetime
start = capture_timestamp()
cutoff = '00:05:00'
dt_cutoff = read_in_cutoff_as_timedelta(cutoff)
if person_detected:
now = capture_timestamp()
dt = now - start
if dt >= dt_cutoff:
# Send notification
send_email_notification()
else:
# Do not Send Notification
print('now: {} | dt: {}'.format(now, dt))
You could use datetime.datetime.utcnow() for timestamp. And datetime.timedelta() for defining dt_cutoff. For reading in a time-string as time you could do this:
tm = datetime.datetime.strptime(cutoff).time()
dt_cutoff = datetime.timedelta(hours = tm.hour, minutes = tm.minute, seconds = tm.second)
I hope this gives you some idea about how to model this.
Additional Resources
https://www.guru99.com/date-time-and-datetime-classes-in-python.html
https://docs.python.org/3/library/datetime.html
https://thispointer.com/python-how-to-convert-a-timestamp-string-to-a-datetime-object-using-datetime-strptime/
Complete Solution
Now, finally if you are in a hurry to use a ready made solution, you may use the following class object as shown. All you would need is to instantiate the class object by specifying your cool-off-period (timer_cutoff) and then call the method is_timeout(). If this returns True, then you send your notification. There is also an obj.istimeout attribute which stores this decision (True/False).
import time
# Set cutoff time to 2 seconds to test the output
# after 5 seconds: expect istimeout = True
# and instantiate the TimeTracker class object.
ttk = TimeTracker(timer_cutoff = '00:00:02') # 'HH:MM:SS'
# Wait for 3 seconds
time.sleep(3)
print('start timestamp: {}'.format(ttk.timestamp_start_str))
print('cutoff timestamp'.format(ttk.timestamp_cutoff_str))
print('timer_cutoff: {}'.format(ttk.timer_cutoff_str))
# Now check if cutoff time reached
ttk.is_timeout()
print('Send Notification: {}'.format(ttk.istimeout))
print('now_timestamp: {}'.format(ttk.timestamp_now_str))
class TimeTracker
Here is the class TimeTracker class:
import datetime
class TimeTracker(object):
def __init__(self,
timer_cutoff = '00:05:00',
cutoff_strformat = '%H:%M:%S'):
self.timer_cutoff_str = timer_cutoff
self.cutoff_strformat = cutoff_strformat
self.timestamp_start, self.timestamp_start_str = self.get_timestamp()
self.dt_cutoff = None # timedelta for cutoff
self.timestamp_cutoff = None
self.timestamp_cutoff_str = None
self.update_timestamp_cutoff()
self.timestamp_now = None
self.timestamp_now_str = None
self.dt_elapsed = None
self.istimeout = False
def get_timestamp(self):
ts = datetime.datetime.utcnow()
tss = str(ts)
return (ts, tss)
def readin_cutoff_as_timedelta(self):
td = datetime.datetime.strptime(self.timer_cutoff_str,
self.cutoff_strformat)
tdm = td.time()
self.dt_cutoff = datetime.timedelta(hours = tdm.hour,
minutes = tdm.minute,
seconds = tdm.second)
def update_timestamp_cutoff(self):
self.readin_cutoff_as_timedelta()
self.timestamp_cutoff = self.timestamp_start + self.dt_cutoff
self.timestamp_cutoff_str = str(self.timestamp_cutoff)
def time_elapsed(self):
self.dt_elapsed = self.timestamp_now - self.timestamp_start
def is_timeout(self):
self.timestamp_now, self.timestamp_now_str = self.get_timestamp()
self.time_elapsed()
if (self.dt_elapsed < self.dt_cutoff):
self.istimeout = False
else:
self.istimeout = True
return self.istimeout

How to check all emails that came in within a time period?

I have the following method get_email() that basically every 20 seconds, gets the latest email and performs a series of other methods on it.
def get_email():
import win32com.client
import os
import time
import datetime as dt
date_time = time.strftime('%m-%d-%Y')
outlook = win32com.client.Dispatch("Outlook.Application").GetNameSpace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
message = messages.GetFirst() # any time calling GetFirst(), you can get GetNext()....
email_subject = message.subject
email_sender = message.SenderEmailAddress
attachments = message.Attachments
body_content = message.body
print ('From: ' + email_sender)
print ('Subject: ' + email_subject)
if attachments.Count > 0:
print (str(attachments.Count) + ' attachments found.')
for i in range(attachments.Count):
email_attachment = attachments.Item(i+1)
report_name = date_time + '_' + email_attachment.FileName
print('Pushing attachment - ' + report_name + ' - to check_correct_email() function.')
if check_correct_email(email_attachment, email_subject, report_name) == True:
save_incoming_report(email_attachment, report_name, get_report_directory(email_subject))
else:
print('Not the attachment we are looking for.')
# add error logging here
break
else: #***********add error logging here**************
print('No attachment found.')
My main question is:
Is there a way I can iterate over every email using the GetNext() function per se every hour instead of getting the latest one every 20 seconds (which is definitely not as efficient as searching through all emails)?
Given that there are two functions: GetFirst() and GetNext() how would I properly have it save the latest checked, and then go through all the ones that have yet to be checked?
Do you think it would be easier to potentially set up a different folder in Outlook where I can push all of these reports to, and then iterate through them on a time basis? The only problem here is, if an incoming report is auto-generated and the time interval between the email is less than 20 seconds, or even 1 second.
Any help at all is appreciated!
You can use the Restrict function to restrict your messages variable to emails sent within the past hour, and iterate over each of those. Restrict takes the full list of items from your inbox and gives you a list of the ones that meet specific criteria, such as having been received in a specified time range. (The MSDN documentation linked above lists some other potential properties you could Restrict by.)
If you run this every hour, you can Restrict your inbox to the messages you received in the past hour (which, presumably, are the ones that still need to be searched) and iterate over those.
Here's an example of restricting to emails received in the past hour (or minute):
import win32com.client
import os
import time
import datetime as dt
# this is set to the current time
date_time = dt.datetime.now()
# this is set to one hour ago
lastHourDateTime = dt.datetime.now() - dt.timedelta(hours = 1)
#This is set to one minute ago; you can change timedelta's argument to whatever you want it to be
lastMinuteDateTime = dt.datetime.now() - dt.timedelta(minutes = 1)
outlook = win32com.client.Dispatch("Outlook.Application").GetNameSpace("MAPI")
inbox = outlook.GetDefaultFolder(6)
# retrieve all emails in the inbox, then sort them from most recently received to oldest (False will give you the reverse). Not strictly necessary, but good to know if order matters for your search
messages = inbox.Items
messages.Sort("[ReceivedTime]", True)
# restrict to messages from the past hour based on ReceivedTime using the dates defined above.
# lastHourMessages will contain only emails with a ReceivedTime later than an hour ago
# The way the datetime is formatted DOES matter; You can't add seconds here.
lastHourMessages = messages.Restrict("[ReceivedTime] >= '" +lastHourDateTime.strftime('%m/%d/%Y %H:%M %p')+"'")
lastMinuteMessages = messages.Restrict("[ReceivedTime] >= '" +lastMinuteDateTime.strftime('%m/%d/%Y %H:%M %p')+"'")
print "Current time: "+date_time.strftime('%m/%d/%Y %H:%M %p')
print "Messages from the past hour:"
for message in lastHourMessages:
print message.subject
print message.ReceivedTime
print "Messages from the past minute:"
for message in lastMinuteMessages:
print message.subject
print message.ReceivedTime
# GetFirst/GetNext will also work, since the restricted message list is just a shortened version of your full inbox.
print "Using GetFirst/GetNext"
message = lastHourMessages.GetFirst()
while message:
print message.subject
print message.ReceivedTime
message = lastHourMessages.GetNext()
You seem to have it running every 20 seconds, so presumably you could run it at a different interval. If you can't run it reliably at a regular interval (which would then be specified in the timedelta, e.g. hours=1), you could save the ReceivedTime of the most recent email checked, and use it to Restrict your search. (In that case, the saved ReceivedTime would replace lastHourDateTime, and the Restrict would retrieve every email sent after the last one checked.)
I hope this helps!
I had a similar question and worked through the above solution. Including another general use example in case other folks find it easier:
import win32com.client
import os
import datetime as dt
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
# setup range for outlook to search emails (so we don't go through the entire inbox)
lastWeekDateTime = dt.datetime.now() - dt.timedelta(days = 7)
lastWeekDateTime = lastWeekDateTime.strftime('%m/%d/%Y %H:%M %p') #<-- This format compatible with "Restrict"
# Select main Inbox
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
# Only search emails in the last week:
messages = messages.Restrict("[ReceivedTime] >= '" + lastWeekDateTime +"'")
print(message.subject)
# Rest of code...
The below solution gives past 30 minutes unread folder mails from outlook and the count of the mails within the past 30 minutes
import win32com.client
import os
import time
import datetime as dt
# this is set to the current time
date_time = dt.datetime.now()
#This is set to one minute ago; you can change timedelta's argument to whatever you want it to be
last30MinuteDateTime = dt.datetime.now() - dt.timedelta(minutes = 30 )
outlook = win32com.client.Dispatch("Outlook.Application").GetNameSpace("MAPI")
inbox = outlook.GetDefaultFolder(6)
# retrieve all emails in the inbox, then sort them from most recently received to oldest (False will give you the reverse). Not strictly necessary, but good to know if order matters for your search
messages = inbox.Items.Restrict("[Unread]=true")
messages.Sort("[ReceivedTime]", True)
last30MinuteMessages = messages.Restrict("[ReceivedTime] >= '" +last30MinuteDateTime.strftime('%m/%d/%Y %H:%M %p')+"'")
print "Current time: "+date_time.strftime('%m/%d/%Y %H:%M %p')
print "Messages from the past 30 minute:"
c=0
for message in last30MinuteMessages:
print message.subject
print message.ReceivedTime
c=c+1;
print "The count of meesgaes unread from past 30 minutes ==",c
Using import datetime, this is what I came up with:
count = 0
msg = messages[len(messages) - count - 1]
while msg.SentOn.strftime("%d-%m-%y") == datetime.datetime.today().strftime("%d-%m-%y"):
msg = messages[len(messages) - count - 1]
count += 1
# Rest of the code

Categories

Resources