Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
Let's say I don't want to limit myself to a single prefix, and instead use a command to change the prefix to whatever I want for each individual server I'm on.
Basically, there would first be a default prefix, for example bl!, and if there was another bot with that prefix, I could do something like bl!prefix .... This would then have the bot read a given text file or json, edit the prefix depending on the guild, and work with that guild and only that guild.
Step 1 - Set up your json files:
First you'll want to make a json file. This file will store the information of message.guild.id as well as the prefix for that guild. In the picture below, you'll see the json file after it was added to multiple servers. If your bot is already in a large number of servers, you may need to add them manually before you can have an automatic system.
Step 2 - Define get_prefix: When you're setting up your discord.py bot, you will usually come across a line of code stating the command_prefix of the bot. To have a custom prefix, you will first need to define get_prefix, which will read from the json file you would have made before.
from discord.ext import commands
def get_prefix(client, message): ##first we define get_prefix
with open('prefixes.json', 'r') as f: ##we open and read the prefixes.json, assuming it's in the same file
prefixes = json.load(f) #load the json as prefixes
return prefixes[str(message.guild.id)] #recieve the prefix for the guild id given
Step 3 - Your bots command_prefix: This is to ensure your bot has a prefix. Instead of using command_prefix = "bl!", you'll be using your previously defined get_prefix.
client = commands.Bot(
command_prefix= (get_prefix),
)
Step 4 - Joining and leaving servers: All this does is manipulate prefixes.json.
#client.event
async def on_guild_join(guild): #when the bot joins the guild
with open('prefixes.json', 'r') as f: #read the prefix.json file
prefixes = json.load(f) #load the json file
prefixes[str(guild.id)] = 'bl!'#default prefix
with open('prefixes.json', 'w') as f: #write in the prefix.json "message.guild.id": "bl!"
json.dump(prefixes, f, indent=4) #the indent is to make everything look a bit neater
#client.event
async def on_guild_remove(guild): #when the bot is removed from the guild
with open('prefixes.json', 'r') as f: #read the file
prefixes = json.load(f)
prefixes.pop(str(guild.id)) #find the guild.id that bot was removed from
with open('prefixes.json', 'w') as f: #deletes the guild.id as well as its prefix
json.dump(prefixes, f, indent=4)
Step 5 - Change Prefix Command:
#client.command(pass_context=True)
#commands.has_permissions(administrator=True) #ensure that only administrators can use this command
async def changeprefix(ctx, prefix): #command: bl!changeprefix ...
with open('prefixes.json', 'r') as f:
prefixes = json.load(f)
prefixes[str(ctx.guild.id)] = prefix
with open('prefixes.json', 'w') as f: #writes the new prefix into the .json
json.dump(prefixes, f, indent=4)
await ctx.send(f'Prefix changed to: {prefix}') #confirms the prefix it's been changed to
#next step completely optional: changes bot nickname to also have prefix in the nickname
name=f'{prefix}BotBot'
client.run("TOKEN")
Edit: This is for any issues you may come across while using this.
How do I use commands.when_mentioned with this?
Why do I get an error whenever someone DMs my bot?
How do I get my bot to respond when mentioned with the guild's set prefix?
Related
I'm working on a discord bot that can send random memes, and I wanted to add a command for continuous feed according to a the amount of memes the user wants, for example /feed number:15, would send 15 memes.
The problem is how to make the option input work on the command, I've find some videos that helped me a little, but due to the other commands being híbrido commands, I can figure out how to make this one working.
This is what's I have so far:
#bot.hybrid_command(name = "feed", with_app_command = True, description = "Feeds a specific number of memes")
#app_commands.guilds(discord.Object(id = guild_id))
async def test1(interaction: discord.Interaction, number: int):
await interaction.response.send_message("It's working!")
The await line, obviously won't do any feed this way, I know, I did the feed part separated and it works, but I just did it this way for testing, so if I type the command and enter a number, it should return me "It's working!", But that never happens, I suppose it's maybe something to do with híbrido commands, but I really don't know, what am I missing?
I don't think the input option is the problem, because I see nothing wrong with it, but the thing that is surely wrong is that you're naming the first argument of the hybrid command interaction, which is wrong and can create confusion; the actual argument it should be is context or ctx. And that object is a discord.ext.commands.Context object.
You should rename it to ctx to avoid confusion.
#bot.hybrid_command(name = "feed", with_app_command = True, description = "Feeds a specific number of memes")
#app_commands.guilds(discord.Object(id = guild_id))
async def test1(ctx: commands.Context, number: int):
await ctx.send("It's working!")
You may want to see the official example here
I wanted to create a website for discord emojis, and I got an idea to make the bot put emoji on the server. Now I did not find any help on youtube so I am here to ask. Hope I get some help
With the vague details you said, I've come up with this
#client.command()
async def add_emoji(ctx)
emoji_path = "" # put your desired file path to get the emoji from
with open(emoji_path, "rb") as f:
await ctx.guild.create_custom_emoji(name="emoji_name", image=f.read())
This opens the emoji in both read mode and binary mode using 'rb'. So it can read the raw bytes of the image since Guild.create_custom_emoji only takes bytes and then passes that raw bytes and a name to uild.create_custom_emoji
I'm new to the coding and stuff and I want to know if there are any tutorials or pages where I can solve this:
I want to make a discord command that looks like this !acz map Lasius flavus where: !acz is the prefix, map is the command and Lasius flavus are two arguments.
Bot responds with message that is a link with the two arguments, that should look like this: https://antmap.coc.tools/images/Lasius.flavus.png
that means that we get a reply on discord that is a picture.
I want to be able to type in my discord !acz map Genus species - and bot responds with https://antmap.coc.tools/images/Genus.species.png where the first letter of Genus is capital, just like in the example.
I'm adding a picture with what I have in mind, where ? is prefix, map is command and Lasius flavus is Genus species
(no, I already asked for the code)
Thanks for your help!
#bot.command(brief="Za map napiš Rod a druh a vyskočí ti mapa výskytu!")
async def map(ctx, *args):
await ctx.channel.send('https://antmap.coc.tools/images/{}.png '.format('.'.join(args)))
this was the way and reading through https://www.programiz.com/python-programming/methods/string/replace helped me! eve tho I didn't use the replace()! I just had to change a few pieces in a code that sent noarguments and arguments:
#bot.command()
async def map(ctx, *args):
await ctx.channel.send('{} arguments: {}'.format(len(args), ', '.join(args)))
I've noticed that when I use autodoc with the ReadTheDoc theme, if I have multiple arguments in my functions they are listed in a bullet list style:
arg1
arg2
...
but if there is only 1 argument then it is not using the bullet list style which is a bit silly to me since it breaks the continuity of the design.
I've found how to remove the disc via CSS to make things more uniform but I actually want to do the opposite and have the disk for the single argument functions.
At this point, I'm not sure it is a CSS change and I do not know how to do that.
I've also noticed the same thing in different docs.
Here is the rendered html:
Here are the 2 methods:
def add_attribute(self, name, index):
"""
:param name: The name attached to the attribute.
:param index: The position of the attribute within the list of attributes. """
print("")
def delete_attribute(self, name):
"""
:param name: The name of the attribute to delete."""
print("")
Here is the my .rst:
API
----------------
.. automodule:: my_module
:members:
Here is the conf.py
extensions = [
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.coverage',
'sphinx.ext.autosummary',
]
templates_path = ['_templates']
language = 'python'
exclude_patterns = []
html_theme = "sphinx_rtd_theme"
html_static_path = ['_static']
autosummary_generate = True
Any idea?
Cheers!
After a lot of digging, I've found a partial workaround for this.
My solution involves manually editing the produced HTML files to insert the missing bullet points.
Required conf.py changes:
# Register hook to run when build is complete
def setup(app):
app.connect('build-finished', on_build_finished)
# Hook implementation
def on_build_finished(app, exception):
add_single_param_bullets("_build/html/index.html")
# Function to actually add the bullet points by overwriting the given HTML file
def add_single_param_bullets(file_path):
print('Add single parameter bullets in {:s}'.format(file_path))
if not os.path.exists(file_path):
print(' File not found, skipping...')
return
lines_enc = []
with open(file_path, 'rb') as f:
for l in f.readlines():
# Check for html that indicates single parameter function
if b'<dd class="field-odd"><p><strong>' in l:
# Work out the encoding if not defined
enc = None
if enc is None:
import chardet
enc = chardet.detect(l)['encoding']
# Decode html and get the parameter information that needs adding
l_dec = l.decode(enc)
l_insert = l_dec.replace('<dd class="field-odd">', '').replace('\r\n', '')
# Add new encoded lines to output
lines_enc.append('<dd class="field-odd"><ul class="simple">'.encode('utf=8'))
lines_enc.append('<li>{:s}</li>'.format(l_insert).encode(enc))
lines_enc.append('</ul>'.encode('utf=8'))
else:
lines_enc.append(l)
# Overwrite the original file with the new changes
with open(file_path, 'wb') as f:
for l in lines_enc:
f.write(l)
In my case, I only have single argument functions in index.html. However, you can register additional files in on_build_finished.
A few things to note:
This only edits the produced HTML files, and doesn't actually solve the underlying problem. I dug through the source for a bit but couldn't find why the bullet points aren't added for single parameter function.
The problem is not just for the RTD theme. It seems to occur with the basic theme as well. So I suspect it's a deeper problem with Sphinx rather than the RTD theme.
The code above somewhat deals with different encodings in the original HTML.
This does not work on the RTD website. As the HTML files are edited in place, and the RTD build outputs the HTML files to a different directory, this solution doesn't seem to work on the RTD website. This is quite annoying. A solution would be to somehow change the RTD build process, or tell RTD to use pre-built HTML sources rather than building its own, but I don't know how to do so.
After spending a few hours working all this out, I actually think it looks better without the bullet points...
I want to ask for user input, but I only want to do it once (possibly save the information within the program), meaning, something like this:
print "Enter your name (you will only need to do this once): "
name = gets.chomp
str = "Hello there #{name}" #<= As long as the user has put their name in the very first
# time the program was run, I want them to never have to put thier name in again
How can I got about doing this within a Ruby program?
This program will be run by multiple users throughout the day on multiple systems. I've attempted to store it into memory, but obviously that failed because from my understand that memory is wiped everytime a Ruby program stops executing.
My attempts:
def capture_user
print 'Enter your name: '
name = gets.chomp
end
#<= works but user has to put in name multiple times
def capture_name
if File.read('name.txt') == ''
print "\e[36mEnter name to appear on email (you will only have to do this once):\e[0m "
#esd_user = gets.chomp
File.open('name.txt', 'w') { |s| s.puts(#esd_user) }
else
#esd_user = File.read('name.txt')
end
end
#<= works but there has to be a better way to do this?
require 'tempfile'
def capture_name
file = Tempfile.new('user')
if File.read(file) == ''
print "\e[36mEnter name to appear on email (you will only have to do this once):\e[0m "
#esd_user = gets.chomp
File.open(file, 'w') { |s| s.puts(#esd_user) }
else
#esd_user = File.read(file)
end
end
#<= Also used a tempfile, this is a little bit over kill I think,
# and doesn't really help because the users can't access their Appdata
You will want to store the username in a file on the local file system. Ruby provides many ways to do this, and we'll explore one in this answer: YAML files.
YAML files are a structured storage file that can store all kinds of different data, and is a good place to store config data. In fact, YAML configuration files are key parts of the largest Ruby projects in existence. YAML gives you a good starting point for supporting future configuration needs, beyond the current one, which is a great way to plan feature development.
So, how does it work? Let's take a look at your requirement using a YAML config:
require 'yaml'
config_filename = "config.yml"
config = {}
name = nil
if file_exists(config_filename)
begin
config = YAML.load_file(config_filename)
name = config["name"]
rescue ArgumentError => e
puts "Unable to parse the YAML config file."
puts "Would you like to proceed?"
proceed = gets.chomp
# Allow the user to type things like "N", "n", "No", "nay", "nyet", etc to abort
if proceed.length > 0 && proceed[0].upcase == "N"
abort "User chose not to proceed. Aborting!"
end
end
end
if name.nil? || (name.strip.length == 0)
print "Enter your name (you will only need to do this once): "
name = gets.chomp
# Store the name in the config (in memory)
config["name"] = name
# Convert config hash to a YAML config string
yaml_string = config.to_yaml
# Save the YAML config string to the config file
File.open(config_filename, "w") do |out|
YAML.dump(config, out)
end
end
Rather than show you the bare minimum to meet your needs, this code includes a little error handling and some simple safety checks on the config file. It may well be robust enough for you to use immediately.
The very first bit simply requires the YAML standard library. This makes the YAML functions work in your program. If you have a loader file or some other common mechanism like that, simply place the require 'yaml' there.
After that, we initialize some variables that get used in this process. You should note that the config_filename has no path information in it, so it will be read from the current directory. You will likely want to store the config file in a common place, such as in ~/.my-program-name/config.yml or C:\Documents and Settings\MyUserName\Application Data\MyProgramName\. This can be done pretty easily, and there's plenty to help, such as this Location to Put User Config Files in Windows and Location of ini/config files in linux/unix.
Next, we check to see if the file actually exists, and if so, we attempt to read the YAML contents from it. The YAML.load_file() method handles all the heavy lifting here, so you just have to ask the config hash that's returned for the key that you're interested in, in this case, the "name" key.
If an error occurs while reading the YAML file, it indicates that the file might possibly be corrupted, so we try to deal with that. YAML files are easy to edit by hand, but when you do that, you can also easily introduce an error that will make loading the YAML file fail. The error handling code here will allow the user to abort the program and go back to fix the YAML file, so that it doesn't simply get overwritten.
After that, we try to see if we've been had a valid name from the YAML config, and if not, we go ahead and accept it from the user. Once they've entered a name, we add it to the config hash, convert the hash to a YAML-formatted string, and then write that string to the config file.
And that's all it takes. Just about anything that you can store in a Ruby hash, you can store in a YAML file. That's a lot of power for storing config information, and if you later need to add more config options, you have a versatile container that you can use exactly for that purpose.
If you want to do any further reading on YAML, you can find some good information here:
YAML in Ruby Tutorial on Robot Has No Heart
Jamming with Ruby YAML on Juixe Techknow
YAML on Struggling with Ruby
While some of these articles are a bit older, they're still very relevant and will give you a jumping off point for further reading. Enjoy!
If you need the name to persist across the user running the script several times, you're going to need to use some sort of data store. As much as I hate flat files, if all you're storing is the user's name, I think this is a valid option.
if File.exist?('username.txt')
name = File.open( 'username.txt', 'r' ) do |file|
name = file.gets
end
else
print "Enter your name (you will only need to do this once): "
name = gets.chomp
File.open( 'username.txt', 'w' ) do |file|
file.puts name
end
end
str = "Hello there #{name}"