(TwitchIO for Python) Invalid syntax error when importing commands - python

I'm trying to follow a TwitchIO tutorial in the documentation here: https://twitchio.readthedocs.io/en/latest/quickstart.html
I jumped to trying the second block of code which adds an event_message so I copied that code. I just want to run the code so I can begin making a chat bot for twitch. Here is that example code that I copied, the only changes I have made to it have been adding my token and initial_channels (Obviously, not included for privacy):
class Bot(commands.Bot):
def __init__(self):
# Initialise our Bot with our access token, prefix and a list of channels to join on boot...
# prefix can be a callable, which returns a list of strings or a string...
# initial_channels can also be a callable which returns a list of strings...
super().__init__(token='ACCESS_TOKEN', prefix='?', initial_channels=['...'])
async def event_ready(self):
# Notify us when everything is ready!
# We are logged in and ready to chat and use commands...
print(f'Logged in as | {self.nick}')
async def event_message(self, message):
# Messages with echo set to True are messages sent by the bot...
# For now we just want to ignore them...
if message.echo:
return
# Print the contents of our message to console...
print(message.content)
# Since we have commands and are overriding the default `event_message`
# We must let the bot know we want to handle and invoke our commands...
await self.handle_commands(message)
#commands.command()
async def hello(self, ctx: commands.Context):
# Here we have a command hello, we can invoke our command with our prefix and command name
# e.g ?hello
# We can also give our commands aliases (different names) to invoke with.
# Send a hello back!
# Sending a reply back to the channel is easy... Below is an example.
await ctx.send(f'Hello {ctx.author.name}!')
bot = Bot()
bot.run()
When I run this code, I receive this error (Changing user to keep info private lol):
Traceback (most recent call last):
File "C:\Users\-USER-\eclipse-workspace\TwitchIO\src\botRD.py", line 13, in <module>
from twitchio.ext import commands
File "C:\Users\-USER-\AppData\Local\Programs\Python\Python36\lib\site-packages\twitchio\__init__.py", line 33, in <module>
from .client import Client
File "C:\Users\-USER-\AppData\Local\Programs\Python\Python36\lib\site-packages\twitchio\client.py", line 36, in <module>
from .http import TwitchHTTP
File "<fstring>", line 1
(await resp.json())
^
SyntaxError: invalid syntax
I don't know where to begin with this issue. I tried going into the twitchio\client.py but it appears to be importing something from .http and I am not sure how to make changes to that or where to go from there. I tried resetting my computer just in case, but that did not fix the error. This is a bit out of my knowledge range, so I would appreciate it if anyone could recommend a good solution. I'm using Eclipse IDE and Windows 10 if that is important.
Thanks!

TwitchIO requires python 3.7+ and it looks like you're running 3.6. I suspect this is your problem.
Source: https://twitchio.readthedocs.io/en/latest/installing.html

Related

How to kick a user using slash commands Discord.py

I'm trying to make my Discord bot kick a member, and send that "user banned because reason" to a specific channel and not the channel the command was used.
The code I'm using:
#bot.slash_command(description = "Kick someone", guild_ids=[1041057700823449682])
#commands.has_permissions(kick_members=True)
#option("member",description = "Select member")
#option("reason",description = "Reason for kick (you can leave this empty)")
async def kick(
ctx,
member: discord.Member,
channel: bot.get_channel(1042042492020863037),
*,
reason=None):
if reason==None:
reason="(no reason)"
await ctx.guild.kick(member)
await ctx.respond("Done :)")
await ctx.channel.send(f'User {member.mention} was kicked because {reason}')
When I try using this code I get a few errors:
Traceback (most recent call last):
File "c:\Users\fonti\Documents\Projetos Python\Bot do Discord\Iniciar Bot.py", line 152, in <module>
async def kick(
File "C:\Users\fonti\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\bot.py", line 905, in decorator
self.add_application_command(result)
File "C:\Users\fonti\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\bot.py", line 127, in add_application_command
command._set_cog(None)
File "C:\Users\fonti\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\commands\core.py", line 603, in _set_cog
self.cog = cog
File "C:\Users\fonti\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\commands\core.py", line 827, in cog
self._validate_parameters()
File "C:\Users\fonti\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\commands\core.py", line 705, in _validate_parameters
self.options: list[Option] = self._parse_options(params)
File "C:\Users\fonti\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\commands\core.py", line 745, in _parse_options
option = Option(option)
File "C:\Users\fonti\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\commands\options.py", line 210, in __init__
self.input_type = SlashCommandOptionType.from_datatype(input_type)
File "C:\Users\fonti\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\enums.py", line 707, in from_datatype
if datatype.__name__ in ["Member", "User"]:
AttributeError: 'NoneType' object has no attribute '__name__'. Did you mean: '__ne__'?
I was trying to send the message...
(f'User {member.mention} was kicked because {reason}')
to a specific channel. If I remove the channel condition, the bot works, but sends this message to the channel the command was used.
I believe the cause of your error is your channel definition inside your kick command definition. Try removing the channel definition from your kick command definition and put it inside the function instead. The way I have it setup on my bot, other than the channel definition, is the same as yours and mine works perfectly
To send it in the channel, instead of using ctx.channel.send, you can use ctx.send. I think that's where you're running into your error. Also here's how I tend to set up my kick command using slash commands so that my answer makes more sense:
#nextcord.slash_command() # I use nextcord, a dpy fork so your setup is gonna be different
#commands.has_permissions(whatever permissions you want)
async def kickuser(self, ctx, member : nextcord.Member, *, reason='None'): # default reason is none so that it is optional in the slash command
# side note for nextcord.Member, having it there makes it so that there's a drop down menu that functions the same way as if you were to # someone in a message. This makes it easier to kick the right person
embed = discord.Embed(description=f'You have been kicked from {ctx.guild} for reason: {reason}')
embed = nextcord.Embed(description=f'{member} has been kicked for reason: {reason}') # setting up embed to send
await ctx.send(embed=embed) # sends the embed in chat letting people know the person has been kicked
await member.kick(reason=reason) # actually kicking the person, this comes after all the other steps so that we are able to mention and direct message them that they have been kicked
Hope this helps
This snip of code uses PyCord ( Confirmed to work by myself )
#discord.default_permissions(kick_members = True)
async def kick(ctx, member : discord.Member, *, reason=None):
await member.kick(reason=reason)
await ctx.respond(f'{member.mention} has been kicked!')
Just get a channel object with bot.get_channel()
Then use the channels send() function to send your message.
Also, : after arguments in function definitions are type hints, they are made for IDEs, if you want to assign a default value, you have to use = instead. (Look at your channel argument)
EDIT
You are using type hints in your code. Type hints are made for IDEs in first place, so they can show you mistakes in your code more easier. But you are „setting“ a value in it with the function, but this is for discord.py None, thats causing your error. Use : for showing which class an argument has to be. But use = for setting a default value, if this argument is not being passed.
def function(type_hint: str, default_value = 0, mixed : int = 10):
print(type_hint, default_value, mixed)
Answer again if you need even further help ;-)

How to send a message to a specific channel as part of a slash command

When I receive a slash command with my bot, I send a modal to a user asking for information.
All this works, however, as part of that, I would also like to send a message to a specific channel on a specific server (guild) to say that a request has been made.
I am having trouble with that second part.
import discord
bot = discord.Bot()
client = discord.Client()
#bot.slash_command(name = "create-trial-request", description = "Create a new trial request from a bbcode template.")
async def trial_request(ctx):
modal = my_modal(title="Fill this in please")
await ctx.send_modal(modal)
class my_modal(discord.ui.Modal):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.add_item(discord.ui.InputText(label="Some Label Name"))
async def callback(self, interaction: discord.Interaction):
request_number = request_number_generator()
# here is where I want to send my message to a specific channel.
# I know the ID of that channel, I just don't know how to send the message there.
code_snippet = format_to_code(bbcode)
request_response = "Created request #" + str(request_number)
await interaction.response.send_message(request_response, ephemeral=True)
I have tried the following (placed where my comments are in the code above):
channel = client.get_channel(6648250855168XXXXX)
await channel.send("Created trial request #" + str(request_number))
...but I get: AttributeError: 'NoneType' object has no attribute 'send'
Obviously the bot has access to the channel, and if I write to it as part of the response to the slash command, it successfully writes there, but I can't seem to make it work on its own.
Is there any way to do what I am trying to?
Thanks for any help.
To do so, 1. import commands aka from discord.ext import commands
Then remove your bot = discord.Bot and edit client do it is client = commands.Bot()
thats what i do
Thanks to #3nws, I now know the answer to this question.
The issue in my case was that I was using both client and bot and should have only been using one of the two (in this instance, bot, since I am using slash commands.
There is a usable bot.getchannel command that does what I wanted.
I hope this helps anyone else with the same issue.
First of all: Don't create 2 bot/client instances. Just use commands.Bot once. That's the reason you made a mistake. You used client instead of bot. Replace it and it should work.
Otherwise, if it would be still None, here are some possible reasons why:
• Not subscribed to the relevant intent.
• Wrong key
 ◦ Remember, IDs are ints, not strings
 ◦ If you're trying to copy an emoji ID, right-clicking the emoji in a message will copy message ID
• Bot not logged in, and trying to grab objects from cache
 ◦ Subvairant, using two client objects.
• Bot cannot "see" the object.
 ◦ It has to be on the server, share a server with the member, etc
 ◦ If you're sharded on separate processes, each process will only have objects for that shard.
• Objects retuned by fetch_x will not have a populated cache.
It doesn't make a huge difference if you do bot.get_channel() or bot.get_guild().get_channel(). You can use both.

Closing Discord bot connection without terminating command line (discord.py)

Purpose:
To add more commands without interrupting other commands
I have been looking around to find a way to roll out updates without interrupting the flow of my bot, since it has some asyncio functions that execute a while after the function has been called
I have tried:
await client.logout()
Above will logout the bot, but also closes the command line. I found this on the discord.py docs.
I am running Windows 10, with Python version 3.9
Any help would be appreciated. Thanks in advance.
If you don't want to kill the current process and instead just want hot-reloading of different functionality, you might want to look into discord.py's extensions feature. Using extensions and cogs will allow you to enable/disable/reload certain features in your bot without stopping it (which should keep tasks running). It's also the built-in method for hot-reloading.
Extensions and cogs are typically used together (though they don't have to be). You can create files for each group of similar commands you want to reload together.
The following code samples should be integrated into your setup. You'll probably also want to add error handling and input checks, but it should give you an idea of what's going on. For a detailed explanation or method options, check out the docs.
# info.py
# ...
# this is just an example
class Info(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command()
async def about(self, ctx):
await ctx.send("something here")
# ...
# extensions need a setup function
def setup(bot):
bot.add_cog(Info(bot))
# main.py
# ...
bot = commands.Bot(
# ...
)
bot.load_extension("info")
bot.run(token)
# reload command
#bot.command()
async def reload(ctx, extension):
# probably want to add a check to make sure
# only you can reload things.
# also handle the input
bot.reload_extension(extension)
to use, you might do something like `prefix!reload info`
Create a new python file in the same directory with the name startup.py for example. Inside this file do the following:
import os
import time
time.sleep(5)
os.system('python YOUR_BOTS_FILE_NAME.py')
Then in the file where your bot's code is add a new command that we are going to call restart for example:
import discord
from discord.ext import commands
import os
#client.command()
#commands.is_owner()
async def restart(ctx):
os.system('python startup.py')
exit()
In the startup.py file, os waits 5 seconds for your bot's file to turn off and then turns it on. The restart command in your bot's file starts the startup file then shuts itself down.
#commands.is_owner()
Makes sure the author of the message is you so people don't restart your bot.
I am developing a bot myself and I have made a shutdown command myself which shuts down the bot without using terminal.
First I would add the code and then explain it.
Code:
myid = <my discord account ID>
#MyBot.command()
async def shutdown(ctx):
if ctx.author.id == myid:
shutdown_embed = discord.Embed(title='Bot Update', description='I am now shutting down. See you later. BYE! :slight_smile:', color=0x8ee6dd)
await ctx.channel.send(embed=shutdown_embed)
await MyBot.logout()
if ctx.author.id != myid:
errorperm_embed = discord.Embed(title='Access Denied!', description='This command is `OWNER` only. You are not allowed to use this. Try not to execute it another time.', color=0xFF0000)
errorperm_embed.set_footer(text=ctx.author)
await ctx.channel.send(embed=errorperm_embed, delete_after=10.0)
I have not added any has_permissions as I don't need it when I using my discord ID to restrict its usage.
Explanation:
I have defined a variable, myid which is equal to my discord account ID.
Check here on how to get user ID:
https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-
I have added a condition that if the ID of the user who used this command is equal to myid or if it is not. If it is equal to my account' ID then it would shutdown the bot otherwise it would show an error to the user.
I have simply used await MyBot.logout() which logs you out and disconnect.
You can place your code in a while True loop.
while True:
client = commands.Bot(command_prefix='!')
async def restart(ctx):
await client.logout()
client.run('token')
You could just replace the current process with the same process, starting anew. You have to flush buffers and close file-pointers beforehand, but that's easy to do:
import os
import sys
from typing import List
def restart(filepointers: List):
# this cleanup part is optional, don't need it if your bot is ephemeral
# flush output buffers
sys.stdout.flush()
sys.stderr.flush()
# flush and close filepointers
for fp in filepointers:
os.fsync(fp)
fp.close()
# replace current process
os.execl(*sys.argv)
Then just call this function with your bot as you would(from the same file).
If you want to update your code you must restart the program.
import os
path = "your .py file path"
#client.command(name="restart")
async def restart_command(ctx):
await client.close()
os.system("python " + path)

Discord Bots in Python

I was following a tutorial on making discord bots when I came across a problem.
I'm trying to connect the client to discord using my token, using the code below:
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
client = discord.Client()
#client.event
async def on_ready():
print(f'{client.user} has connected to Discord!')
client.run(TOKEN)
`
I also have a separate .env file which looks like this:
DISCORD_TOKEN="***" ## yeah I'm not giving anyone my token
I ran this in the command prompt and I got this error:
File "discordBot.py", line 15, in <module>
client.run(***)
NameError: name '***' is not defined ## Once again not actually my token
The only thing I've noticed is that the name that isn't defined isn't actually the full token and it stops after a ".", I tried putting the entire thing in quotes on the .env file but that didn't fix it.
This is a client side problem. It would be useful if you could post in your question what service you are using to host the bot. It is a problem with either your server or something with your API key. Make sure the key is right. I don't see anything wrong with your program, if you are saving the responses in a file on your computer make sure its in RB or WB format. Other formats will cause your program to glitch. Best of luck with your bot! This also might be because you are asking the user for an API key and he does not have one. You should switch your async data.

Create Server using Discord Bot

If we look at the Guild Create event in the discord dev documentation, we see the following:
I have a couple of questions about this. First of all, I am not sure exactly when I can create a server using a bot account. Following the "when a user is initially connecting" section, I attempted to place the server creation into the on_ready function, like so:
import discord
import asyncio
import bot.client.getkey as _getkey
from bot.utils import get_owner
class ImABot(discord.Client):
async def on_ready(self):
ts = await self.create_server("test")
inv = await self.create_invite(ts.default_channel)
await self.send_message(get_owner()) #get owner simply gets the user who owns the bot, me.
Bot = ImABot()
Bot.run(_getkey.key())
However, I get the following error:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/discord/client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "/Users/edl/Desktop/ /Programming/Github/Democracy-Bot/demobot/client/client.py", line 22, in on_ready
inv = await self.create_invite(ts.default_channel)
File "/usr/local/lib/python3.6/site-packages/discord/client.py", line 2628, in create_invite
data = yield from self.http.create_invite(destination.id, **options)
AttributeError: 'NoneType' object has no attribute 'id'
I assume this means the server was not created. I hope someone can give me information on when creating a server will work, and whether or not it is possible in the first place. Thanks!
Guilds no longer have default channels, so it is best to avoid using them.
To get the first created channel for the server, your best bet is to use
discord.utils.get(new_server.channels, type=discord.ChannelType.text)
Or if you use discord.py rewrite, then
new_server.text_channels[0]
Then you can create the invite from that channel.

Categories