Discord.py 2.0 on Repl.it - python

I want to make discord bot that uses buttons I downloaded library and ewerything. It works but because I am using Repl.it i have to use web + uptimerobot so it will never turn off.
Even after that it seems every few hours to quickly turn off and back on, so it will lose all data except databases. I was thinking i can solve this by saving message to database and inside event on_ready delete message and create new one but bot after going off and on cant delete message from varriable.
Code for web:
from flask import Flask
from threading import Thread
app = Flask('')
#app.route('/')
def home():
return "I am working!"
def run():
app.run(host='0.0.0.0',port=8080)
def keep_alive():
t = Thread(target=run)
t.start()
I would like to solve turning off and on problem + it will be helpfull to fix deleting message from database after reseting.
Side question: Do you guys know any easy way to assign instead of reference value to varriable because of circular reference/dependency.

Make a JSON file to save the data and just have it not delete the data on restart. have it autosave when a user uses it.
kinda like this
#client.command()
async def add(ctx, meme_):
def add_memes(meme, file="memes.json"):
with open(file, "r+") as fw:
j = json.load(fw)
j["memes"].append(meme)
with open(file, "w+") as wp:
wp.write(json.dumps(j))
try:
with open("memes.json", "r"):
pass
except:
with open("memes.json", "w+") as wp:
wp.write('{"memes" : []}')
finally:
add_memes(meme_)
await ctx.send("Done!")
but this is a command for saving memes

Related

I don't want the Telegram bot send repeated messages

i'm learning Python!
I'm building a Bot that searches for products in an e-commerce, the bot will always send a message with a price predefined by me!
The bot does a new search every 5 minutes, but I don't want send the same product again in the next message, i did some research but found nothing that fits what i need.
Sorry for my bad english! I hope you understand what I meant.
Code:
def send_to_telegram(message):
apiToken = 'xxxxx'
chatID = '-xxxx'
bot = telebot.TeleBot(apiToken)
if placa and fabricante_1 in marca:
bot.send_message(
chat_id=chatID, text=f"<b>Modelo:</b> {marca} \n<b>Fabricante:</b> {fabricante_1}\n<b>Preço a vista:</b> R$ {valor_preco_avista} \n<b>Preço a prazo:</b> R$ {valor_preco_prazo} \n<b>Loja:</b> {loja} \n\n<b>Link Produto:</b> {url_completa}", parse_mode='HTML')
I tried some modules like: schedule, time
But in the end the bot sends the duplicate message anyway.
I managed to solve my problem, I'll leave the solution for anyone who has a similar doubt in the future!
To avoid sending repeated messages with a Telegram bot with Python, you can store a record of the messages that have already been sent and check against this record before sending a new message.
import os
import pickle
sent_messages_file = "sent_messages.pickle"
if os.path.exists(sent_messages_file):
with open(sent_messages_file, "rb") as f:
sent_messages = pickle.load(f)
else:
sent_messages = []
def send_message(mensagem):
apiToken = 'XXXXXXXXXX'
chatID = 'XXXXXXXX'
bot = telebot.TeleBot(apiToken)
bot.send_message(
chat_id=chatID, text=message, parse_mode='HTML')
sent_messages.append(message)
with open(sent_messages_file, "wb") as f:
pickle.dump(sent_messages, f)
In this code, the sent_messages list is saved to a file using the pickle module. Before sending a new message, the code checks if the sent_messages_file exists. If it exists, the code loads the sent_messages list from the file. If it does not exist, the code creates an empty sent_messages list. After sending a message, the code saves the updated sent_messages list to the file.
This way, when you run the code again, it will load the sent_messages list from the file, and only send new messages that have not been sent before.

Need to know timestamp of when Discord bot was last functioning

I am making a discord bot and I want for it to be able to take actions based on it being online.
However, I am running into the issue that I can't seem to figure out an easy way to know when the discord bot last went offline (which I need to know).
Currently I was trying to look into a way by using the activity of the bot in order to figure out when the bot goes offline since activities theoretically have timestamps from in the documentation. I don't know if this method would work this way but certainly I can't get it working and have the following trackback:
Traceback (most recent call last):
File "C:\Users\carol\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\client.py", line 409, in _run_event
await coro(*args, **kwargs)
File "C:\Users\carol\Code\Personal\visabot\visa_bot.py", line 128, in on_ready
success =await self.add_visa_after_offline()
File "C:\Users\carol\Code\Personal\visabot\visa_bot.py", line 103, in add_visa_after_offline
async for active in visabot.activity.timestamps:
AttributeError: 'NoneType' object has no attribute 'timestamps'
Here is the code for the specific function that errors:
async def add_visa_after_offline(self) -> bool:
guild = await self.get_guild()
visabot = await self.get_visabot_member()
async for active in visabot.activity.timestamps:
print(active)
And here is my intents:
client = MyClient(GUILD_ID, SPAM_CHANNEL, MAIN_CHANNEL,BOT_ID, intents=discord.Intents.all())
client.run(BOT_TOKEN)
After combing through the documentation I didn't see anything that looked like this did exactly. Please let me know if there is any specific functionality for this or if there is some easier alternative way to know when the bot last went offline.
IF AT ALL POSSIBLE, I want to avoid having any kind of database, even a super simple one. So simply combing through discord stuff to infer last went offline would be much superior to solutions that use a database.
I mostly thought it would be an intent problem so I tried to let it have all intents. It could also be a permissions token problem and there is some permission its lacking. Looking at them though I am not sure that there is.
Additionally, I know that the visabot object is definitely the bot since .name, .id, and other methods work properly. Also the activity on the robot is "Watching" which I know since I can see it watching in the discord app.
Thanks.
I solved it by getting lazy and instead storing a variable of "LAST_ACTION" since my bot performs actions periodically. This will be a pretty accurate time of when the bot was last functional.
Relevant features:
Used a Json file for storage. Don't know if it was very optimal but it worked.
def get_json_data(self) -> dict:
with open(self.tracker_file, 'r') as openfile:
data = json.load(openfile)
return data
def update_json(self, new_data: dict) -> bool:
data = self.get_json_data()
data.update(new_data)
with open(self.tracker_file, "w") as outfile:
json.dump(data, outfile)
For initializing the client object I added:
def __init__(self, data, *args, **kwargs):
super().__init__(*args, **kwargs)
...
...
...
self.tracker_file = "var_tracker.json"
if not os.path.exists(self.tracker_file):
now = get_now()
# this will make it not really function well if you need to use this the first time
init_data = {'LAST_TIME_OF_ACTION': str(now), 'ASSIGNED': []}
with open(self.tracker_file, 'w') as openfile:
json.dump(init_data, openfile)
data = self.get_json_data()
self.timestamp_before_online = data['LAST_TIME_OF_ACTION']
Then I made a function to reference the "self.timestamp_before_online" and get the timestamp since I have to convert from datetime to string to put in the json then back to datetime to use while coding. It was a little annoying to figure out though:
def get_now():
return datetime.datetime.now((datetime.timezone.utc))
def str_datetime_to_datetime_obj(str_datetime: str) -> datetime.datetime:
datetime_obj = datetime.datetime.strptime(str_datetime,
"%Y-%m-%d %H:%M:%S.%f%z")
return datetime_obj
def search_last_around(self) -> datetime.datetime:
last_around = str_datetime_to_datetime_obj(self.timestamp_before_online)
return last_around
Finally, I simply updated the json for the "LAST_TIME_OF_ACTION "as the last line in my task loop:
#tasks.loop(seconds=300)
async def purge_visas_background_task(self):
print("Attempting Purge at {}".format(get_now()))
success = await self.purge_all_overstayed_visa()
if success:
pass
else:
await self.send_to_spam('Purge has failed')
await self.report_error()
now = get_now()
self.update_json({'LAST_TIME_OF_ACTION': str(now)})
Not super elegant or compact but it works for my purposes so I thought I'd share it in case it helped anyone else.

Using a Python websocket server as an async generator

I have a scraper that requires the use of a websocket server (can't go into too much detail on why because of company policy) that I'm trying to turn into a template/module for easier use on other websites.
I have one main function that runs the loop of the server (e.g. ping-pongs to keep the connection alive and send work and stop commands when necessary) that I'm trying to turn into a generator that yields the HTML of scraped pages (asynchronously, of course). However, I can't figure out a way to turn the server into a generator.
This is essentially the code I would want (simplified to just show the main idea, of course):
import asyncio, websockets
needsToStart = False # Setting this to true gets handled somewhere else in the script
async def run(ws):
global needsToStart
while True:
data = await ws.recv()
if data == "ping":
await ws.send("pong")
elif "<html" in data:
yield data # Yielding the page data
if needsToStart:
await ws.send("work") # Starts the next scraping session
needsToStart = False
generator = websockets.serve(run, 'localhost', 9999)
while True:
html = await anext(generator)
# Do whatever with html
This, of course, doesn't work, giving the error "TypeError: 'Serve' object is not callable". But is there any way to set up something along these lines? An alternative I could try is creating an 'intermittent' object that holds the data which the end loop awaits, but that seems messier to me than figuring out a way to get this idea to work.
Thanks in advance.
I found a solution that essentially works backwards, for those in need of the same functionality: instead of yielding the data, I pass along the function that processes said data. Here's the updated example case:
import asyncio, websockets
from functools import partial
needsToStart = False # Setting this to true gets handled somewhere else in the script
def process(html):
pass
async def run(ws, htmlFunc):
global needsToStart
while True:
data = await ws.recv()
if data == "ping":
await ws.send("pong")
elif "<html" in data:
htmlFunc(data) # Processing the page data
if needsToStart:
await ws.send("work") # Starts the next scraping session
needsToStart = False
func = partial(run, htmlFunc=process)
websockets.serve(func, 'localhost', 9999)

Command unblocker

I have this cog blocker that puts a guild id and the command blocked in a .json file. When the command is used, it will check to see if this information is in the .json file, if it is, it will not run the command. Although, I do not know how to make it so it takes out the command or guild id so the command can be used again. Please help!
#commands.command()
#has_permissions(administrator=True)
async def block(self, ctx, cmd: str):
with open(os.path.dirname(__file__) + f'/json/data.json','r+') as f:
data=json.load(f)
if not str(ctx.message.guild.id) in data:
data.update({str(ctx.message.guild.id): []})
data[str(ctx.message.guild.id)].append(cmd)
else:
if cmd not in data[str(ctx.message.guild.id)]:
data[str(ctx.message.guild.id)].append(cmd)
await ctx.send(embed=discord.Embed(description=f'{ctx.author.mention} has banned the command **{cmd}**',color=65535))
else:
await ctx.send(embed=discord.Embed(description=f'{ctx.author.mention} **{cmd}** is already banned', color=65535))
self.write("data", data)
If you can give me the code, that would be greatly appreciated.
Ok so you can remove the guild id simply by
# Do a if id in data.keys() b4 to make sure there wont be a KeyError
with open('json/data.json','r+') as f: # u don't need the f"" or __file__
data = json.load(f)
del data[str(ctx.guild.id)]
# save json do bomb stuff
Now deleting a specific command
#Load json like the previous snippet
if cmd in data[str(ctx.guild.id)]:
del data[str(ctx.guild.id)][data[str(ctx.guild.id)].index(cmd)]
#save json
and its also advisable to use a db instead of json(its very useful for seldom changed data) but if its a small project json would do fine

Discord.py adding a command to store data

Basically I have a discord bot that auto leaves servers unless its in a database. I was wondering how I could make a command to add to that database and remove? Heres my current code
server=[878537556332015626,
884958773913985054,
869615568838357052]
#client.event
async def on_guild_join(guild):
if guild.id in server:
pass
elif guild.id not in server:
await guild.leave()
So where it says "server=[stuff here]" how could i make a command to add servers ids to that database? Maybe like ';addserver server-id' and it'd add it to that database, and also a remove command. Like ';removeserver server-id' ive attempted multiple times but it didnt work, neither did it show an error.
Or help me make this into a json file!
If your goal is to make a command for your bot such as ;addserver server-id, it would be quite simple. Here's an example for checking the validity of the ID and writing it to a servers.json file if it's valid:
#client.command()
async def addserver(ctx, message = None):
if client.get_guild(int(message)): # If the bot is in a guild with this ID, returns True
serverFile = json.loads(open('servers.json', 'r').read()) # Reading Json file to a dict
serverFile['servers'].append(int(message)) # Appending new ID to the dict
with open('servers.json', 'w') as writeFile:
json.dump(serverFile, writeFile, indent=4) # Writing dict to Json file
else:
pass # Do whatever you want with this part, maybe an error message would be sent to the command sender or something.
And servers.json should be laid out like this:
{
"servers": [
]
}
Reading the list of server IDs from the file would look like this:
json.loads(open('servers.json', 'r').read())['servers'] # Returns a list of IDs in the file.
And to remove an ID from the list:
#client.command()
async def removeserver(ctx, message = None):
serverList = json.loads(open('servers.json', 'r').read())['servers']
for id in serverList:
if int(message) == id:
serverList.remove(id)
serverDict = {}
serverDict['servers'] = serverList
with open('servers.json', 'w') as writeFile:
json.dump(serverDict, writeFile, indent=4)
The part you'll likely find the most value out of is the if client.get_guild(int(message)) line, as it will let you know whether the ID is valid or not. This can be applied to new functions that could clean up your server list if need be.

Categories

Resources