how to get messages by date using TELETHON? - python

how to get message that posted today using TELETHON
I'm using the below code
date_of_post = datetime.datetime(2019, 12, 24)
with TelegramClient(name, api_id, api_hash) as client:
for message in client.iter_messages(chat , offset_date = date_of_post):
print(message.sender_id, ':', message.text)

offset_date is used to get messages prior to that date. So you should use the day after:
async def get_messages_at_date(chat, date):
result = []
tomorrow = date + datetime.timedelta(days=1)
async for msg in client.iter_messages(chat, offset_date=date):
if msg.date < date:
return result
result.append(msg)

The point made by #Lonami is valid - offset_date is used to get messages prior to that date. However, the docs describe another argument called reverse that you can supply to iter_messages :
reverse (bool, optional):
If set to True, the messages will be returned in reverse order (from oldest to newest, instead of the default newest to oldest). This also means that the meaning of offset_id and offset_date parameters is reversed, although they will still be exclusive.
So, if you use it like this:
for message in client.iter_messages(chat, reverse = True, offset_date = date_of_post):
print(message.sender_id, ':', message.text)
it should work as you expected.

Related

loop attribute cannot be acessed in non-async contexts with discord.py

When I try to run this code
`
import json
import os
import random
from pprint import pprint
import aiohttp
import discord
import requests
from discord.ext import commands
from dotenv import load_dotenv
from mojang import api
# Functions
# Sends a Get request to a given url
def get_info(call):
r = requests.get(call)
return r.json()
# Get the sum of coins in the bazaar
def get_bazaar_buy_order_value(bazaar_data):
sum_coins = 0
price_increase_threshold = 2
buy_order_values = []
# For every product
for item_name, item_data in bazaar_data.get("products", {}).items():
item_sum_coins = 0
# For every buy order
for idx, buy_order in enumerate(item_data.get("buy_summary", [])):
# If its the best price
if(idx == 0):
item_expected_value = buy_order.get("pricePerUnit", 0)
item_sum_coins += buy_order.get("amount", 0) * buy_order.get("pricePerUnit", 0)
# If its not the best price, check for reasonable price
else:
if(buy_order.get("pricePerUnit", 0) < (item_expected_value * price_increase_threshold)):
item_sum_coins += buy_order.get("amount", 0) * buy_order.get("pricePerUnit", 0)
buy_order_values.append((item_name, item_sum_coins))
sum_coins += item_sum_coins
sort_bazaar_buy_orders_by_value(buy_order_values)
return sum_coins
# Sorts and displays a list of buy order items by total value
def sort_bazaar_buy_orders_by_value(buy_order_values):
# Sort items by values
buy_order_values.sort(key = lambda x: -x[1])
# Display items and values
for (item_name, item_sum_coins) in buy_order_values:
print(f"{item_name.ljust(30, ' ')} | {round(item_sum_coins):,}")
return
# Returns Bazaar data
def get_bazaar_data():
return get_info("https://api.hypixel.net/skyblock/bazaar")
# Returns a specific item from the Bazaar
def get_bazaar_item():
return
# Returns auction info from player uuid
def get_auctions_from_player(uuid):
return get_info(f"https://api.hypixel.net/skyblock/auction?key={API_KEY}&player={uuid}")
# Returns current mayor/election data
def get_election_data():
return get_info(f"https://api.hypixel.net/resources/skyblock/election")
# Returns a list of player profiles
def get_profiles_data():
return get_info(f"https://sky.shiiyu.moe/api/v2/profile/{example_uuid}")
# Returns player UUID when prompted with the name
async def get_uuid(name):
return get_info(f"https://sky.shiiyu.moe/api/v2/profile/{name}")
# Discord Functions / Vars
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
client = discord.Client(intents=discord.Intents.default())
intents = discord.Intents.all()
bot = commands.Bot(command_prefix='/',intents=intents)
# Hypixel Vars
Item = "Diamond"
API_FILE = open("API_KEY.json","r")
example_name = "4748"
example_uuid = "147ab344d3e54952b74a8b0fedee5534"
uuid_dashed = "147ab344-d3e5-4952-b74a-8b0fedee5534"
API_KEY = json.loads(API_FILE.read())["API_KEY"]
example_player_uuid = "147ab344d3e54952b74a8b0fedee5534"
auctions_player_url = f"https://api.hypixel.net/skyblock/auction?key={API_KEY}&player={example_player_uuid}"
# Commands
#bot.command(name='bazaar', description = "Gives a detailed readout of a certain item in the bazaar", brief = "Get data of an item in bazaar")
async def bazaar(ctx):
await ctx.send(get_bazaar_data())
await ctx.send(API_KEY)
#bot.command(name="bazaartotal", description = "Show the total amount of coins on the bazaar at any given point", brief = "Shows the amount of coins in the bazaar")
async def baztot(ctx):
await ctx.send(get_bazaar_buy_order_value(get_bazaar_data()))
#bot.command(name = "apikey", description = "Gives 4748's API key, make sure to remove me once publicly availible!", brief = "API Key")
async def key(ctx):
await ctx.send(API_KEY)
#bot.command(name = "profiles", description = 'Get a list of player profiles and data about them', brief = "List player profiles")
async def prof(ctx):
await ctx.send("Username to check?")
message = client.wait_for('message', check=lambda m: m.user == ctx.user)
username = str(message.content)
uuid = get_uuid(username)
pprint(uuid)
await ctx.send(uuid)
bot.run(TOKEN)
I get this error
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: loop attribute cannot be accessed in non-async contexts. Consider using either an asynchronous main function and passing it to asyncio.run or using asynchronous initialisation hooks such as Client.setup_hook
Anyone have a fix for this? The bot runs normally, but once I try to run /profiles it gives me that error. Also, other commands work fine, but when I try to access an api with a
Changed my code multiple times, putting the get_uuid command in async, and googling for a few hours. any help is appreciated!
Your Bot variable is called bot, but you're using client in your wait_for statement.
You've got both a discord.Client ("client") and a commands.Bot ("bot") instance. This doesn't make a whole lot of sense. If you only need Client features then use Client, if you want Bot features then use Bot. You can't use both at the same time.
Also, wait_for is a coroutine, so you should await it.
# Yours:
message = client.wait_for('message', check=lambda m: m.user == ctx.user)
# ^^^^^^^
# Missing await keyword & wrong bot variable
# Correct:
message = await bot.wait_for(...)
# ^^^^^ ^^^
Docs: https://discordpy.readthedocs.io/en/stable/api.html?highlight=wait_for#discord.Client.wait_for
PS requests is blocking and will make your whole bot freeze. Consider looking into an asynchronous http library like aiohttp.
try to do this:
message = await client.wait_for('message', check=lambda m: m.user == ctx.user)
I advise you to remove the 'name' from the slash command argument, it's better to just name the function and add an additional check for the channel 'm.channel == ctx.channel' to wait_for and split the file into several

discord.py invalid form of body

code:
#bot.command(name="whois")
async def whois(ctx,user:nextcord.Member=None):
if user==None:
user=ctx.author
rlist = []
for role in user.roles:
if role.name != "#everyone":
rlist.append(role.mention)
b = ", ".join(rlist)
embed = nextcord.Embed(colour=user.color,timestamp=ctx.message.created_at)
embed.set_author(name=f"User Info - {user}"),
embed.set_thumbnail(url=user.avatar.url),
embed.set_footer(text=f'Requested by - {ctx.author},
icon_url=ctx.author.avatar.url)
embed.add_field(name='ID:',value=user.id,inline=False)
embed.add_field(name='Name:',value=user.display_name,inline=False)
embed.add_field(name='Created at:',value=user.created_at,inline=False)
embed.add_field(name='Joined at:',value=user.joined_at,inline=False)
embed.add_field(name='Bot?',value=user.bot,inline=False)
embed.add_field(name=f'Roles:({len(rlist)})',value=''.join([b]),inline=False)
embed.add_field(name='Top Role:',value=user.top_role.mention,inline=False)
await ctx.send(embed=embed)
error:
nextcord.ext.commands.errors.CommandInvokeError: Command raised an exception: HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
In embeds.0.fields.5.value: Must be 1024 or fewer in length
how do I fix? because my code is shorter than 2000
I assume it's an issue with you putting too much text into the embed's field. How many roles are in rlist? If it's more than 47 (46.54 ~ 1024 / 22), then it's going to get mad.
You'll need to put a cap on the number of characters/roles that can appear in that field.
edit: Also, why do you cast b to a list and then join it? You're just turning a string into a list, then back into the exact same string.
In set_footer you forgot to add a second ' so it makes the footer way to long for discord to handle

Asyncpg INSERT query for timestamp returns ERROR syntax error at or near "18"

Hi I'm getting an error that occurs when trying to add the timestamp. I keep getting the error asyncpg.exceptions.PostgresSyntaxError: syntax error at or near "18" and this line end_date = duration + dt.datetime.now(bst) # timestamp seems to be the culprit however I'm unsure why this is the case.
Here is what I'm working with:
if time is not None:
conn = await asyncpg.connect(DATABASE_URL)
async with conn.transaction():
await conn.fetch(f"SELECT time FROM blacklist WHERE username={member.id}")
duration = find_date(time)
bst = pytz.timezone('Europe/London')
end_date = duration + dt.datetime.now(bst) # timestamp
fmt_date = end_date.strftime("%#d %b %Y, at %I:%M%p")
await conn.fetch(f"UPDATE blacklist SET time={end_date} WHERE username={member.id}")
await member.add_roles(restricted, reason=f'Restricted role added by {author.name}')
await member.remove_roles(members)
await conn.close()
msg = f"{member} has been restricted until {fmt_date}."
embed = discord.Embed(title="Restricted", description=msg, colour=author.color)
await ctx.send(embed=embed)
return
You don't pass the arguments in f-strings when dealing with SQL queries, the syntax for query arguments in asyncpg is $n, also I see that you're using the Connection.fetch method but you're simply updating the table, I'd suggest you to use Connection.execute
Your code fixed:
end_date = # Should be a datetime.datetime instance
await conn.execute("""
UPDATE blacklist
SET time = $1
WHERE username = $2
""", end_date, member.id)
Removing timezone awareness
end_date = # `datetime.datetime` instance
naive = end_date.replace(tzinfo=None)
await conn.execute("""
UPDATE blacklist
SET time = $1
WHERE username = $2
""", naive, member.id)
References:
Connection.execute
PS: Don't create a new connection everytime you want to use it, normal practice is to have one long-term database connection

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

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