I need a command that shows the list of the commands with cooldowns and the time remaining to use the command again. Is it possible especially putting the command in a cog? If yes, can you help me how to do the command?
It's definitely possible, to get all commands in cooldown and their remaining time. However, discord.py does not give direct access to it, and thus we have to abuse private class variables to get them
Code:
import datetime
#client.command()
async def cooldowns(ctx: commands.Context):
string = ""
for command in client.walk_commands():
dt = ctx.message.edited_at or ctx.message.created_at
current = dt.replace(tzinfo=datetime.timezone.utc).timestamp()
bucket = command._buckets.get_bucket(ctx.message, current)
if not bucket:
continue
retry_after = bucket.update_rate_limit(current)
if retry_after:
string += f"{command.name} - {retry_after} Seconds\n"
else:
string += f"{command.name} - `READY`\n"
string = string or "No commands are on cooldown"
await ctx.send(string)
Here is what the above code will look like for commands not on cooldown:
It will follow:
command 1 - READY
command 2 - READY
...
command n - READY (and so on)
and, here is how it will look if commands are on cooldown:
Explaining how the code works:
string = ""
We initialize an empty string in a string variable, this is the string we will add our lines to and send it all at once.
for command in bot.walk_commands():
dt = ctx.message.edited_at or ctx.message.created_at
current = dt.replace(tzinfo=datetime.timezone.utc).timestamp()
bucket = command._buckets.get_bucket(ctx.message, current)
bot.walk_command() is a method that gives us a generator object, which is essentially a generator of all the command objects the bot has stored (i.e: everything under #bot.command(), #commands.command, and #commands.group)
dt is just storing the current time the message was created at, it's a datetime object
message.created_at is a time naive offset and so we bind a timezon to it with its replace method and then we get a timestamp of that with the timestamp() method of datetime.datetime objects
all of the things we did is a waste, the third line is the meat and potatoes of what we want. command._buckets.get_bucket is an internal, we provide the current message object and the timestamp we created earlier.
This gives us our Cooldown object (The one you create with #commands.cooldown(1, 5, commands.BucketType.user), it basically yields what is inside the ()) [This is None for commands with no cooldowns]
here is what it looks like, just for understanding.
if not bucket:
continue
retry_after = bucket.update_rate_limit(current)
if retry_after:
string += f"{command.name} - {retry_after} Seconds\n"
else:
string += f"{command.name} - `READY`\n"
await ctx.send(string)
if not bucket:
continue
If a bucket is not found, command does not have cooldown, meaning we can skip over it.
retry_after = bucket.update_rate_limit(current)
This basically gets us our remaining time (it's a float)
returns None if it's not on cooldown
if retry_after:
string += f"{command.name} - {retry_after} Seconds\n"
else:
string += f"{command.name} - `READY`\n"
the if statement checks if it returned a float, if yet then command is on cooldown and we add the command name along side the cooldown time
the else is for if it's not on cooldown, then it adds the command name and READY along side it.
And at the end we send the entire string.
In my case, I only have one command that had cooldown, which is spotify.
Creating a command that shows all cooldowns is possible. You can do this with a for loop and the is_on_cooldown() function.
Here is an example of a command to return a list of commands and if they are on cooldown:
#client.command(pass_content=True)
async def cooldowns(ctx):
cooldown_string = ""
for command in client.commands:
if command.is_on_cooldown(ctx):
cooldown_string += f"\n{command} - **Time Left:** {command.get_cooldown_retry_after(ctx)}MS"
await ctx.send(cooldown_string)
From here you can add condition statements to only show commands on cooldown.
Related
I am trying to send a REST API call to retrieve a lot of data. Now this data is returned in JSON format and is limited to 2000 records each call. However, if there are more than 2000 records then there is a key called nextRecordsUrl with a link to an endPoint with the next 2000 records.
And this pattern continues until there are less than 2000 records in a single call and then nextRecordsUrl is undefined.
I have this loop that essentially pushes the data to an array and then calls the endpoint listed in the nextRecordsUrl key.
do {
for (var i in arrLeads.records) {
let data = arrLeads.records[i];
let createDate = new GMT(data.CreatedDate, "dd-MM-YYYY");
let fAssocDate = new GMT(data['First_Campaign_assoc_date__c'],"dd-MM-YYYY");
let lAssocDate = new GMT(data['Last_Campaign_assoc_date__c'], "dd-MM-YYYY");
let convDate = new GMT(data.ConvertedDate, "dd-MM-YYYY");
leads.push([data.FirstName, data.LastName, data.Id, data.Email, data.Title, data['hs_contact_id__c'], createDate, data.Company, data.OwnerId, data.Country, data['Region_by_manager__c'], data.Status, data.Industry, data['Reason_for_Disqualifying_Nurture__c'], data['Reason_for_Disqualifying_Dead__c'], data['Lead_Type__c'], data['First_Campaign_Name__c'], data['First_CampaignType__c'], fAssocDate, data['Last_Campaign_Name__c'], lAssocDate, data['Last_Campaign_Type__c'], convDate, data.ConvertedAccountId, data.ConvertedContactId, data.ConvertedOpportunityId]);
}
var arrLeads = getQueryWithFullEndPoint(arrLeads.nextRecordsUrl);
} while (arrLeads.nextRecordsUrl != null && arrLeads.nextRecordsUrl != undefined);
I used to use the regular while loop, but it caused obvious problems in that it wouldn't run at all if the initial call had an empty nextRecordsUrl field.
But this also has an issue. While the first iteration works well, the last one does not because it makes the call on the next iteration before the loop checks the nextRecordsUrl field.
So essentially, it will loop through all the records normally. But when it gets to the last one, it has already run the last endPoint and will break the loop, because now the 'nextRecordsUrl key is empty. So the last iteration will be ignored.
I thought of moving the call to after the check, right after the do {, but that will cause problems for the first iteration.
I also thought about duplicating the for loop after the do, but I prefer a cleaner solution that doesn't involve duplicating code.
So how do I write this code in a way that takes into account the first iteration, even if there no second iteration and the last iteration, where it has records but has an empty nextRecordsUrl, without having to double up the code?
I actually managed to solve this with a small tweak in the code.
My issue is that I getting the next batch before I could test the original, and so I was always 1 batch short.
The solutions was actually to define a variable at the beginning of each loop arrLeadsNext = arrLeads.nextRecordsUrl;
and have the while statement check this variable.
So it looks like this:
do {
var arrLeadsNext = arrLeads.nextRecordsUrl;
for (var i in arrLeads.records) {
let data = arrLeads.records[i];
let createDate = new GMT(data.CreatedDate, "dd-MM-YYYY").process();
let fAssocDate = new GMT(data['First_Campaign_assoc_date__c'], "dd-MM-YYYY").process();
let lAssocDate = new GMT(data['Last_Campaign_assoc_date__c'], "dd-MM-YYYY").process();
let convDate = new GMT(data.ConvertedDate, "dd-MM-YYYY").process();
leads.push([data.FirstName, data.LastName, data.Id, data.Email, data.Title, data['hs_contact_id__c'], createDate, data.Company, data.OwnerId, data.Country, data['Region_by_manager__c'], data.Status, data.Industry, data['Reason_for_Disqualifying_Nurture__c'], data['Reason_for_Disqualifying_Dead__c'], data['Lead_Type__c'], data['First_Campaign_Name__c'], data['First_CampaignType__c'], fAssocDate, data['Last_Campaign_Name__c'], lAssocDate, data['Last_Campaign_Type__c'], convDate, data.ConvertedAccountId, data.ConvertedContactId, data.ConvertedOpportunityId]);
}
var arrLeads = (arrLeadsNext != null) ? getQueryWithFullEndPoint(arrLeadsNext) : '';
} while (arrLeadsNext != null && arrLeadsNext != undefined);
So the check at the end hasn't been changed by the next API call for the next batch as it will only change once the next iteration of the loop begins.
I am trying to write a Gatling script where I read a starting number from a CSV file and loop through, say 10 times. In each iteration, I want to increment the value of the parameter.
It looks like some Scala or Java math is needed but could not find information on how to do it or how and where to combine Gatling EL with Scala or Java.
Appreciate any help or direction.
var numloop = new java.util.concurrent.atomic.AtomicInteger(0)
val scn = scenario("Scenario Name")
.asLongAs(_=> numloop.getAndIncrement() <3, exitASAP = false){
feed(csv("ids.csv")) //read ${ID} from the file
.exec(http("request")
.get("""http://finance.yahoo.com/q?s=${ID}""")
.headers(headers_1))
.pause(284 milliseconds)
//How to increment ID for the next iteration and pass in the .get method?
}
You copy-pasted this code from Gatling's Google Group but this use case was very specific.
Did you first properly read the documentation regarding loops? What's your use case and how doesn't it fit with basic loops?
Edit: So the question is: how do I get a unique id per loop iteration and per virtual user?
You can compute one for the loop index and a virtual user id. Session already has a unique ID but it's a String UUID, so it's not very convenient for what you want to do.
// first, let's build a Feeder that set an numeric id:
val userIdFeeder = Iterator.from(0).map(i => Map("userId" -> i))
val iterations = 1000
// set this userId to every virtual user
feed(userIdFeeder)
// loop and define the loop index
.repeat(iterations, "index") {
// set an new attribute named "id"
exec{ session =>
val userId = session("userId").as[Int]
val index = session("index").as[Int]
val id = iterations * userId + index
session.set("id", id)
}
// use id attribute, for example with EL ${id}
}
Here is my answer to this:
Problem Statement: I had to repeat the gatling execution for configured set of times, and my step name has to be dynamic.
object UrlVerifier {
val count = new java.util.concurrent.atomic.AtomicInteger(0)
val baseUrl = Params.applicationBaseUrl
val accessUrl = repeat(Params.noOfPagesToBeVisited,"index") {
exec(session=> {
val randomUrls: List[String] = UrlFeeder.getUrlsToBeTested()
session.set("index", count.getAndIncrement).set("pageToTest", randomUrls(session("index").as[Int]))
}
).
exec(http("Accessing Page ${pageToTest}")
.get(baseUrl+"${pageToTest}")
.check(status.is(200))).pause(Params.timeToPauseInSeconds)
}
So basically UrlFeeder give me list of String (urls to be tested) and in the exec, we are using count (AtomicInteger), and using this we are populating a variable named 'index' whose value will start from 0 and will be getAndIncremented in each iteration. This 'index' variable is the one which will be used within repeat() loop as we are specifying the name of counterVariable to be used as 'index'
Hope it helps others as well.
While trying to make a simple register/signup client only application for a personal project. I'm trying to load a list of users from a file, and compare them to a possible username. If the username already exists, the program will give them an error.
Here is a condensed clone of the code:
u1 = str(input("Input username: "))
t = open("userlistfile","r")
userlist = t.readline()
y = 0
for x in range(0, len(userlist)-1):
if userlist[y] == u1:
print("\n !Error: That username (",u1,") is already taken!")
y += 1
The user list is stored in a file so that it can opened, appended, and saved again, without being stored in the program. My current issue is that the userlist is saved as a string rather than an array. Is there a better way to do this? Thank you.
EDIT: Thanks to user lorenzo for a solution. My Friends are telling me to post a quick (really simple) copy of a for you guys who can't figure it out.
New code:
u1 = str(input("Input username: "))
t = open("userlistfile","r")
userlist = t.read() #Readline() changed to Read()
userlist = userlist.split('--') #This line is added
y = 0
for x in range(0, len(userlist)-1):
if userlist[y] == u1:
print("\n !Error: That username (",u1,") is already taken!")
y += 1
Example text file contents:
smith123--user1234--stacky
This line will seperate the string at the ('--') seperators and append each split part into an array:
userlist = userlist.split('--')
#Is used so that this (in the text file)
Smith123--user1234--stacky
#Becomes (in the program)
userlist = ['Smith123','user1234','stacky']
Sorry for the long post... Found it very interesting. Thanks again to Lorenzo :D.
userlist = t.readline()
reads one line from the file as a string. Iterating, then, gets characters in the string rather than words.
You should be able to get a list of strings (words) from a string with the split() method of strings or the more general re.split() function.
I'm new to F# and trying to dive in first and do a more formal introduction later. I have the following code:
type Person =
{
Id: int
Name: string
}
let GetPeople() =
//seq {
use conn = new SQLiteConnection(connectionString)
use cmd = new SQLiteCommand(sql, conn)
cmd.CommandType <- CommandType.Text
conn.Open()
use reader = cmd.ExecuteReader()
let mutable x = {Id = 1; Name = "Mary"; }
while reader.Read() do
let y = 0
// breakpoint here
x <- {
Id = unbox<int>(reader.["id"])
Name = unbox<string>(reader.["name"])
}
x
//}
let y = GetPeople()
I plan to replace the loop body with a yield statement and clean up the code. But right now I'm just trying to make sure the data access works by debugging the code and looking at the datareader. Currently I'm getting a System.InvalidCastException. When I put a breakpoint at the point indicated by the commented line above, and then type in the immediate windows reader["name"] I get a valid value from the database so I know it's connecting to the db ok. However if I try to put reader["name"] (as opposed to reader.["name"]) in the source file I get "This value is not a function and cannot be applied" message.
Why can I use reader["name"] in the immediate window but not in my fsharp code? How can I use string indexing with the reader?
Update
Following Jack P.'s advice I split out the code into separate lines and now I see where the error occurs:
let id = reader.["id"]
let id_unboxed = unbox id // <--- error on this line
id has the type object {long} according to the debugger.
Jack already answered the question regarding different syntax for indexing in F# and in the immediate window or watches, so I'll skip that.
In my experience, the most common reason for getting System.InvalidCastException when reading data from a database is that the value returned by reader.["xyz"] is actually DbNull.Value instead of an actual string or integer. Casting DbNull.Value to integer or string will fail (because it is a special value), so if you're working with nullable columns, you need to check this explicitly:
let name = reader.["name"]
let name_unboxed : string =
if name = DbNull.Value then null else unbox name
You can make the code nicer by defining the ? operator which allows you to write reader?name to perform the lookup. If you're dealing with nulls you can also use reader?name defaultValue with the following definition:
let (?) (reader:IDataReader) (name:string) (def:'R) : 'R =
let v = reader.[name]
if Object.Equals(v, DBNull.Value) then def
else unbox v
The code then becomes:
let name = reader?name null
let id = reader?id -1
This should also simplify debugging as you can step into the implementation of ? and see what is going on.
You can use reader["name"] in the immediate window because the immediate window uses C# syntax, not F# syntax.
One thing to note: since F# is much more concise than C#, there can be a lot going on within a single line. In other words, setting a breakpoint on the line may not help you narrow down the problem. In those cases, I normally "expand" the expression into multiple let-bindings on multiple lines; doing this makes it easier to step through the expression and find the cause of the problem (at which point, you can just make the change to your original one-liner).
What happens if you pull the item accesses and unbox calls out into their own let-bindings? For example:
while reader.Read() do
let y = 0
// breakpoint here
let id = reader.["id"]
let id_unboxed : int = unbox id
let name = reader.["name"]
let name_unboxed : string = unbox name
x <- { Id = id_unboxed; Name = name_unboxed; }
x
I just have a question about writing a function that will search a directory for the most recent log in a directory. I currently came up with one, but I'm wondering if there is a better (perhaps more proper) way of doing this.
I'm currently using hdsentinel to create logs on computer and placing the log in a directory. The logs are saved like so:
/directory/hdsentinel-computername-date
ie. C:/hdsentinel-owner-2010-11-11.txt
So I wrote a quick script that loops through certain variables to check for the most recent (within the past week) but after looking at it, I'm question how efficient and proper it is to do things this way.
Here is the script:
String directoryPath = "D:"
def computerName = InetAddress.getLocalHost().hostName
def dateToday = new Date()
def dateToString = String.format('%tm-%<td-%<tY', dateToday)
def fileExtension = ".txt"
def theFile
for(int i = 0; i < 7; i++) {
dateToString = String.format('%tY-%<tm-%<td', dateToday.minus(i))
fileName = "$directoryPath\\hdsentinel-$computerName-$dateToString$fileExtension"
theFile = new File(fileName)
if(theFile.exists()) {
println fileName
break;
} else {
println "Couldn't find the file: " + fileName
}
}
theFile.eachLine { print it }
The script works fine, perhaps it has some flaws. I felt I should go ahead and ask what the typical route is for this type of thing before I continue with it.
All input is appreciated.
Though a bit messy, you could implement a multi-column sort via the 'groupBy' method (Expounding on Aaron's code)..
def today = new Date()
def recent = {file -> today - new Date(file.lastModified()) < 7}
new File('/yourDirectory/').listFiles().toList()
.findAll(recent)
.groupBy{it.name.split('-')[1]}
.collect{owner, logs -> logs.sort{a,b -> a.lastModified() <=> b.lastModified()} }
.flatten()
.each{ println "${new Date(it.lastModified())} ${it.name}" }
This finds all logs created within the last week, groups them by owner name, and then sorts according to date modified.
If you have files other than logs in the directory, you may first need to grep for files containing 'hdsentinel.'
I hope this helps.
EDIT:
From the example you provided, I cannot determine if the least significant digit in the format:
C:/hdsentinel-owner-2010-11-11.txt
represents the month or the day. If the latter, sorting by file name would automatically prioritize by owner, and then by date created (without all of the chicanery of the above code).
For Instance:
new File('/directory').listFiles().toList().findAll(recent).sort{it.name}
Hopefully this helps some..This sorts a given path by date modified in a groovier way. The lists them out.
you can limit the list, and add other conditions in the closure to get the desired results
new File('/').listFiles().sort() {
a,b -> a.lastModified().compareTo b.lastModified()
}.each {
println it.lastModified() + " " + it.name
}
As I was trying to solve a similar problem, learnt a much cleaner approach.
Define a closure for sorting
def fileSortCondition = { it.lastModified() }
And File.listFiles() has other variation which accepts FileFilter and FilenameFilter in Java, and these interfaces has a single method called accept, Implement the interface as a closure.
def fileNameFilter = { dir, filename ->
if(filename.matches(regrx))
return true
else
return false
} as FilenameFilter
And lastly
new File("C:\\Log_Dir").listFiles(fileNameFilter).sort(fileSortCondition).reverse()
Implement FileFilter interface if filtering is to be done by File attributes.
def fileFilter = { file ->
if(file.isDirectory())
return false
else
return true } as FileFilter