so currently I try to program a calculator but I hit a roadblock. Well, I watched a tutorial by Glowstik on how to make calculator discord bot but it seems that his code doesn't work anymore. So, can anybody suggest changes that I can make to the code to make the bot work? Thanks in advance.
Here is the code :
import discord
import os
from discord.ext import commands, tasks
from online import keep_alive
from discord_slash import SlashCommand
from itertools import cycle
from discord_components import *
import datetime
client = commands.Bot(command_prefix="!")
slash = SlashCommand(client, sync_commands=True)
status = cycle([
" Unanswered Question of Life", " Self - Referential Paradox",
" Near-infinite density?", " Dark matter ?",
" Measurement of the speed of light in one straight line",
" Schrodinger's cat ???",
"The light side of Discord is the path of many unnatural abilities"
])
#client.event
async def on_ready():
print("I have logged in as {0.user}".format(client))
status_swap.start()
DiscordComponents(client)
buttons = [[
Button(style=ButtonStyle.grey, label='1'),
Button(style=ButtonStyle.grey, label='2'),
Button(style=ButtonStyle.grey, label='3'),
Button(style=ButtonStyle.blue, label='+'),
Button(style=ButtonStyle.red, label='Clear')
],
[
Button(style=ButtonStyle.grey, label='4'),
Button(style=ButtonStyle.grey, label='5'),
Button(style=ButtonStyle.grey, label='6'),
Button(style=ButtonStyle.blue, label='-'),
Button(style=ButtonStyle.red, label='Exit')
],
[
Button(style=ButtonStyle.grey, label='7'),
Button(style=ButtonStyle.grey, label='8'),
Button(style=ButtonStyle.grey, label='9'),
Button(style=ButtonStyle.blue, label='×'),
Button(style=ButtonStyle.red, label='←')
],
[
Button(style=ButtonStyle.grey, label='00'),
Button(style=ButtonStyle.grey, label='0'),
Button(style=ButtonStyle.grey, label='.'),
Button(style=ButtonStyle.blue, label='÷'),
Button(style=ButtonStyle.red, label='=')
]]
def calculator(exp):
o = exp.replace('×', '*')
o = o.replace('÷', '/')
result = " "
try:
result = str(eval(o))
except:
result = "An error occurs"
return result
#client.command()
async def operator(ctx):
m = await ctx.send(content="Loading calculator")
expression = "None"
delta = datetime.datetime.utcnow() + datetime.timedelta(minutes = 5)
e = discord.Embed(title="Basic Operation Calculator",description=expression)
await m.edit(components=buttons, embed=e)
while True :
res = await client.wait_for("button click")
while m.created_at < delta :
if res.author.id == int(res.message.embeds[0].title.split("|")[1]) and res.message.embeds[0].timestamp < delta:
expression = res.message.embeds[0].description
if expression == "None" or expression == 'An error occurs':
expression = ''
elif res.component.label == 'Exit':
await res.respond(content='Calculator Closed. Thanks for using Basic Operation Calculator.',type=7)
break
elif res.component.label == "←":
expression = expression[:-1]
elif res.component.label == 'Clear':
expression = None
elif res.component.label == '=':
expression += calculator(expression)
else:
expression = res.component.label
f = discord.Embed(title='Basic Operation Calculator',description=expression)
await res.respond(content='', embed = f, component=buttons, type=7)
# tasks.loop(minutes=5)
async def status_swap():
await client.change_presence(activity=discord.Game(next(status)))
keep_alive()
client.run(os.getenv('MATH_VAR'))
This code looks like it was taken from https://www.youtube.com/watch?v=3BGcgSm9sv0
which coincidentally I have also used.
Your problem arises from your embed title because it's based around your title having a '|' followed by the user's ID.
try changing the embed title from "Basic Operation Calculator" to f"{ctx.message.author}'s calculator|{ctx.message.author.id}" and tell me if that works. I've modified the code I'm using slightly, so this might be wrong, I'm going off of memory here. If that does work, also try f"Basic Operation Calculator|{ctx.message.author.id}" if you want to keep your title name.
This isn't needed but if you also want to add a sliver of text after the '|', you need to change the split statement in
if res.author.id == int(res.message.embeds[0].title.split("|")[1]) and res.message.embeds[0].timestamp < delta:
to the '|' and any text (thats not the ID) directly after.
so if I want to add "ID: " after, then the split statement would be "| ID: ". Sorry if this sounds confusing, the way this code works is very specific, and I'm pretty new to Stack Overflow. Let me know if you run into any errors or have any questions. If nothing else works I'm happy to send you my current code I'm using since It's only slightly modified. Next time you post a question, try your best to be as specific as possible with your question.
Related
This is my first trying creating a discord bot. I want it to work like this:
User:
/roll 2d20+4
Then my bot will roll 2d20, save the sum and the bigger. Finally, it will print like this:
Output:
Bigger: 16+4=20 | Sum: 23+4=27.
This is the code I have:
import hikari
import lightbulb
import random
disc_token="..."
server_id="..."
bot=lightbulb.BotApp(
token=disc_token,
default_enabled_guilds=int(server_id)
)
#bot.command
#lightbulb.command('roll',"Role dados (exemplo: 2d20+5)")
#lightbulb.implements(lightbulb.SlashCommand)
async def roll(ctx,play):
die_split=play.split("d")
final_split=die_split[1].split("+")
n_die=int(die_split[0])
die=int(final_split[0])
extra=int(final_split[1])
play=[n_die,die,extra]
best_roll=0
sum_roll=0
for i in range(n_die):
actual_roll=random.randint(1,die)
sum_roll=sum_roll+actual_roll
if(actual_roll>best_roll):
best_roll=actual_roll
sum_roll=sum_roll+extra
extra_roll=best_roll+extra
play_print='Bigger: '+str(best_roll)+'+'+str(extra)+'='+str(extra_roll)+' | Sum: '+str(sum_roll)
await ctx.respond(play_print)
bot.run()
What is happening:
The bot is working (I tested some basic commands, like "Hello World" and works fine), but when I try to run the /roll command, for the discord user it shows:
"! The app did not answered".
At my terminal, the error was displayed like this:
Traceback (most recent call last):
File "C:\Users\MateusRochaQSOFT\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\lightbulb\app.py", line 1163, in invoke_application_command
await context.invoke()
File "C:\Users\MateusRochaQSOFT\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\lightbulb\context\base.py", line 328, in invoke
await self.command.invoke(self)
File "C:\Users\MateusRochaQSOFT\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\ligh raise new_exc
lightbulb.errors.CommandInvocationError: An error occurred during command 'roll' invocation
What could be wrong?
I think you're defining your options wrong for using the lightbulb package. You have to define the options using the #lightbulb.option decorator and can then access them via ctx.options. Where the name of the option is the property you can get.
#bot.command
#lightbulb.option("play", "dice roll")
#lightbulb.command('roll',"Role dados (exemplo: 2d20+5)")
#lightbulb.implements(lightbulb.SlashCommand)
async def roll(ctx):
play = ctx.options.play
die_split = play.split("d")
final_split = die_split[1].split("+")
n_die = int(die_split[0])
die = int(final_split[0])
extra = int(final_split[1])
play = [n_die,die,extra]
best_roll = 0
sum_roll = 0
for i in range(n_die):
actual_roll = random.randint(1, die)
sum_roll = sum_roll + actual_roll
if actual_roll > best_roll:
best_roll = actual_roll
sum_roll = sum_roll + extra
extra_roll = best_roll + extra
play_print = f"Bigger: {best_roll} + {extra} = {extra_roll} | Sum: {sum_roll}"
await ctx.respond(play_print)
bot.run()
Found in the documentation here. You can access the play variable using ctx.options.play.
I've also formatted the code to use PEP8 formatting and I used f-strings in place of the string concatenation that you were doing.
You can also use the pass_options parameter in #lightbulb.command to pass all the defined options (via decorators) as keyword arguments.
Like this:
#bot.command
#lightbulb.option("play", "dice roll")
#lightbulb.command('roll',"Role dados (exemplo: 2d20+5)", pass_options=True)
#lightbulb.implements(lightbulb.SlashCommand)
async def roll(ctx, play):
This way you don't need to do play = ctx.options.play.
Hello I recently made a help command using discord_components buttons (I know they arent fully supported by Discord.py) but I still went ahead. The problem is that whenever I run the command and receive the Buttons to click on, they alway say "This Interaction Failed". I can't seem to find what's wrong. Please help.
Thanking You,
NightMX.
import discord
from discord.ext import commands
from discord_components.component import ButtonStyle
from discord_components import DiscordComponents, Button, Select, SelectOption
from discord_components.interaction import InteractionType
class BotCommands(commands.Cog):
def __init__(self, client):
self.client = client
#commands.command()
async def helpv2(self, ctx):
funbutton = Button(style=ButtonStyle.grey, label="Fun Commands", id="funcmds")
monkedevbutton = Button(style=ButtonStyle.grey, label="Attachment Commands", id="monkecmds")
# utilitybutton = Button(style = ButtonStyle.grey, label = "3", id = "embed3")
funembed = discord.Embed(title="Fun Commands", colour=discord.Colour.orange())
funembed.add_field(name="k.joke", value="Sends a Random joke from PyJokes")
monkedevembed = discord.Embed(title="Fun Commands", colour=discord.Colour.blurple())
monkedevembed.add_field(name="k.dog", value="Sends a Random Dog Fact")
monkedevembed.add_field(name="k.monkey", value="Sends a Monkey's Picture")
monkedevembed.add_field(name="k.bird", value="Sends a Bird's Picture")
await ctx.send(
"Kola's Beta Help Command!",
components=[[funbutton, monkedevbutton]]
)
buttons = {
"funcmds": funembed,
"monkedcmds": monkedevembed
}
while True:
event = await self.bot.wait_for('button_click')
if event.channel is not ctx.channel:
return
if event.channel == ctx.channel:
response = buttons.get(event.component.id)
if response is None:
await event.channel.send(
"Something went Wrong"
)
if event.channel == ctx.channel:
await event.respond(
type=InteractionType.ChannelMessageWithSource, embed=response
)
def setup(client):
client.add_cog(BotCommands(client))
should be content="something" instead of embed = response
beginner here.
CONTEXT: I just started coding last month. I am a translation and interpretation student, and have kind of a thesis project. I chose to create a assistive program for translators that would make things like looking up words online easier etc. I have greatly benefitted from StackOverflow and similar sites in creating a few codes that work well -for a beginner at least.
What I need is a way to combine my small codes into one bigger program in which the user can choose which function of my project they would like to use. I have, for example, a timer that gives the user notification to take a break in given intervals and an integrated API for machine translation.
I looked online for similar problems but they did not include commands for working on user demand. What I have in my mind is a program that works like:
When you run the code, it asks you "Which tool would you like to use? Translation or timer?" and runs the appropriate command on user demand. Here are my two codes for example: First is my machine translation API and the latter is my notification timer.
import requests
print('Welcome to the translation tool. Below are some language codes for you to use:')
print('English -> en, Turkish -> tr, Spanish -> es, French -> fr, Russian -> ru, Chinese -> zh.')
print('***You can type "e" into the source word box to exit the program.***')
sourcelang = str(input('Which language would you like to translate FROM?: '))
targetlang = str(input('Which language would you like to translate TO?: '))
while 1==1:
url = "https://systran-systran-platform-for-language-processing-v1.p.rapidapi.com/translation/text/translate"
word = str(input('Which word would you like to look up?: '))
querystring = {"source":sourcelang, "target":targetlang, "input":word}
headers = {
'x-rapidapi-key': "8a96426f46msh7c7b8957d8b6d49p12c046jsnf7904623bf34",
'x-rapidapi-host': "systran-systran-platform-for-language-processing-v1.p.rapidapi.com"
}
response = requests.request("GET", url, headers=headers, params=querystring)
print(response.text)
if word == str('e'):
print ('Thanks for using this software. Have a good one.')
break
and
import winrt.windows.ui.notifications as notifications
import winrt.windows.data.xml.dom as dom
import time
userinput = input('Specify time intervals in minutes: ')
print('Timer started, will notify to take a break in specified intervals.')
while 1 == 1:
notifString = """
<toast>
<visual>
<binding template='ToastGeneric'>
<text>Time to Get Up</text>
<text>Stretch Your Legs</text>
</binding>
</visual>
</toast>
"""
notifTime = float(userinput)
notifManager = notifications.ToastNotificationManager
notif = notifManager.create_toast_notifier()
xmlNotif = dom.XmlDocument()
xmlNotif.load_xml(notifString)
def givenotification(t) :
time.sleep(t*60)
notif.show(notifications.ToastNotification(xmlNotif))
givenotification(notifTime)
Please do let me know if my question is too vague or you need some more details. Thanks in advance.
P.S: Additional help to make my code and program look better would be much appreciated :)
You can use the execfile('filename.py') in Python2
or
You can use the exec(open('filename.py').read()) in Python3
import os
choice=input("Which tool would you like to use? Translation or timer?")
if choice.lower() == "translation":
exec(open('translation.py').read())
elif choice.lower() == "timer":
exec(open('timer.py').read())
else:
print("Only 2 options available(Translation/Timer")
Or you can simply import the another translation and timer file as a module and execute their functions in the if else block
You could just wrap both the tools into their own methods, then give the user the choice at the start of the program (in main() with this example):
import requests
import winrt.windows.ui.notifications as notifications
import winrt.windows.data.xml.dom as dom
import time
def Translate():
print('Welcome to the translation tool. Below are some language codes for you to use:')
print('English -> en, Turkish -> tr, Spanish -> es, French -> fr, Russian -> ru, Chinese -> zh.')
print('***You can type "e" into the source word box to exit the program.***')
sourcelang = str(input('Which language would you like to translate FROM?: '))
if word == str('e'):
print ('Thanks for using this software. Have a good one.')
break
targetlang = str(input('Which language would you like to translate TO?: '))
while 1==1:
url = "https://systran-systran-platform-for-language-processing-v1.p.rapidapi.com/translation/text/translate"
word = str(input('Which word would you like to look up?: '))
querystring = {"source":sourcelang, "target":targetlang, "input":word}
headers = {
'x-rapidapi-key': "8a96426f46msh7c7b8957d8b6d49p12c046jsnf7904623bf34",
'x-rapidapi-host': "systran-systran-platform-for-language-processing-v1.p.rapidapi.com"
}
response = requests.request("GET", url, headers=headers, params=querystring)
print(response.text)
def givenotification(t) :
time.sleep(t*60)
notif.show(notifications.ToastNotification(xmlNotif))
def Timer():
userinput = input('Specify time intervals in minutes: ')
print('Timer started, will notify to take a break in specified intervals.')
while 1 == 1:
notifString = """
<toast>
<visual>
<binding template='ToastGeneric'>
<text>Time to Get Up</text>
<text>Stretch Your Legs</text>
</binding>
</visual>
</toast>
"""
notifTime = float(userinput)
notifManager = notifications.ToastNotificationManager
notif = notifManager.create_toast_notifier()
xmlNotif = dom.XmlDocument()
xmlNotif.load_xml(notifString)
givenotification(notifTime)
def main():
choice = ''
while choice != '3':
choice = input('Which tool would you like to use? 1->Translate, 2->Timer, 3->Quit')
if choice == '1':
Translate()
elif choice == '2':
Timer()
main()
I also moved the if word == str('e): up so you don't have to wait for a http request just to quit the program.
You can run a file using os module. You can use that and if else block to check user input and run the file you want to. Here is an example:
import os
choice=input("Which tool would you like to use? Translation or timer?")
if choice.lower() == "translation":
os.startfile("Translation.py")#Your file path here
elif choice.lower() == "timer":
os.startfile("Timer.py")
else:
print("Only 2 options available(Translation/Timer")
I am new at python so sry if question is stupid but i hope u guys will help me.
Bot not updating status every 5 sec(i put more time like 5 min and it didn't work too). It shows number of servers and not changing to second status.
from discord.ext import commands, tasks
from itertools import cycle
#tasks.loop( seconds = 12 )
async def changeStatus():
status = cycle( [f' on { len(client.guilds) } servers', '~help'] )
await client.change_presence( activity = discord.Activity( type = discord.ActivityType.playing, name = next(status) ) )
#client.event
async def on_ready():
print( 'bot connected' )
changeStatus.start()
Since
status = cycle( [f' on { len(client.guilds) } servers', '~help'] )
is called everytime you call your function, it will be reinterpreted, which means that the next() function always return the first element. To fix this, you will need a different approach. For example, create a global iteration-variable and declare you cycle-list as only a list.
iterationPosition = 0
#tasks.loop( seconds = 12 )
async def changeStatus():
status = [f' on { len(client.guilds) } servers', '~help']
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.playing, name=status[iterationPosition]))
iterationPosition = 0 if (iterationPosition == len(status) - 1) else (iterationPosition + 1)
You will need to keep track if you've reached the end of your list. This is done by the last line of code.
It is not changing the status as you got a Presence Update rate-limit of 5/60secs per-session. These rate-limits are dynamic so don't get too close to them
status = cycle( [f' on { len(client.guilds) } servers', '~help'] ) This line should be outside of the changeStatus function as whenever it is called, it makes [f' on { len(client.guilds) } servers the first item.
Writing a python script that sets up pickup games. srv.connect() will time out if the IP/RCON are put in wrong and/or the server is down altogether. I do not want the discord bot to crash just because a server is down so I am trying to use a try/except to keep the bot going. A person can start a pickup by typing !pickup size 4 servername ... and if srv.connect() can not get a handshake with the server, itll time out and send a message in discord saying it can not find the server. Then they could do !pickup size 4 servername2 and itll work. Problem right now is that after doing !pickup size 4 servername it seems stuck on saying the rcon/ip is down even though servername2 should be running just fine. Any help?
if(message.content.startswith('!pickup size')):
if(pickupActive == 0):
x = message.content.split()
if(len(x) >= 4):
pServer = x[3]
if(pServer in servers):
srv = Console(host=servers[pServer][0], port= servers[pServer][1], password=servers[pServer][2])
try:
srv.connect()
servercfg = srv.execute("servercfgfile")
#print(servers[pServer][3])
if (servers[pServer][3] in servercfg): #and (pickupActive == 0)): #change "server.cfg" to whatever your server configurtion filename is for pickup play. keep the quotations
totalPlayers = [" "] * int(x[2])
initialPlayerCount = len(totalPlayers)
pickupLeader = message.author.name
totalPlayers.insert(playerCount, pickupLeader)
totalPlayers.remove(" ")
PopulateTable()
await message.channel.send("```Pickup Game Starting! " + pickupLeader + " is the leader for the Pickup Game! Come join in! type '!pickup add' to join```")
await message.channel.send("```" + msg + "```")
pickupActive = 1
else:
await message.channel.send("`" + servers[pServer][4] + "`")
except:
await message.channel.send("Can not connect to the server for rcon..")
srv.disconnect()
else:
await message.channel.send("`Please specify the size of the pickup and a valid server name..`")
else:
await message.channel.send("`Proper formatting not used, please say !pickup size # server (Example: !pickup size 3 nwo)`")
else:
await message.channel.send("`Already a pickup game in progress, please add up to the current pickup game..`")