I want to make my bot respond in a single message, but because of the for loop it replies 10 times in 10 different messages. My code is:
#bot.command(name='translate', help='- Translates text to a random language!')
async def translating(ctx, thing):
translator = Translator()
for i in range(0, 10):
translation = translator.translate(thing, dest=str(random.choice(list_language_unicodes)))
await ctx.reply(translation.text)
The code takes a string and translates it to 10 random different languages if somebody was interested. My problem is only with the bot NOT replying in a SINGLE message. I'm a beginner so i probably won't understand much but still.
Solution: I concatenated the strings and reset at the end of the loop because the translations kept stacking:
for i in range(0, 10):
translation = translator.translate(thing, dest=str(random.choice(list_language_unicodes)))
finalny_tekst = finalny_tekst + translation.text + "\n"
await ctx.reply(finalny_tekst)
finalny_tekst = ""
Thanks for assistance!
Related
I am trying to collect the participants' responses (i.e., the first key they press on the keyboard) and their reaction time (i.e., the time elapsed since the presentation of a picture and the response). I am using the KbQueueXXX functions in Psychtoolbox, but I am not sure if I am using them properly.
Thanks for your help.
Dear Psychtoolbox users,
I wrote a script to run a 2AFC task where participants must respond within a fixed deadline of 2 seconds. If participants respond earlier, then they move to the subsequent trial straight away. To collect their responses (i.e., the first key they pressed and their RT), I coded a custom function that incorporates KbQueueCheck(). I am now reviewing and debugging my script but I have some doubts about the KbQueueXXX functions. I would be grateful if you could give me some feedback.
Task script
In a mixture of code and pseudocode, here is what I am doing.
KbQueueCreate(KEYBOARD, KEYS_OF_INTEREST);
KbQueueStart(KEYBOARD);
% TRIALS' LOOP STARTS
for iTrial = 1:nTrials
KbQueueFlush(KEYBOARD);
% show stimulus and record its onset and offset times
respDeadline = stimulusOffset + 2;
collectResponse(KEYBOARD, respDeadline, stimulusOnset); % <- KbQueueCheck() here!
end
% TRIALS' LOOP ENDS
KbQueueStop(KEYBOARD);
KbQueueRelease(KEYBOARD);
Custom function
Below is my custom function collectResponse() where I embedded KbQueueCheck().
function [choice, rt, choiceTime, firstPress, keyCodes, pressed] = collectResponse(deviceIndex, untilTime, targetOnset)
%% LOOK FOR KEYPRESSES
pressed = false;
while pressed == false && GetSecs <= untilTime
[pressed, firstPress] = KbQueueCheck(deviceIndex);
end
%% PROCESS KEYPRESSES
if pressed == false % NO KEYS WERE PRESSED
keyCodes = NaN;
firstPress = NaN;
choiceTime = NaN;
choice = NaN;
rt = NaN;
else % ONE OR MORE KEYS WERE PRESSED
keyCodes = find(firstPress > 0); % all keypresses
choiceTime = min(firstPress(keyCodes)); % ts of first keypress
choice = find(firstPress == choiceTime); % first keypress
if length(choice) > 1
% handle simultaneous keypresses
choice = -999;
end
rt = choiceTime - targetOnset; % reaction time
end
end
My questions
I am not sure whether I am calling KbQueueXXX functions correctly and whether they are in the expected position.
Shall I keep KbQueueCreate()/KbQueueStart() and KbQueueStop()/KbQueueRelease() respectively before and after the trials’ loop?
Shall I rather just KbQueueStart(), KbQueueCheck(), and KbQueueStop() at each trial iteratively?
Is it OK to KbQueueFlush() at the beginning of each trial, before the presentation of a new stimulus?
Is my custom function collectResponse() fit for the purpose I described at the top of this post?
Thank you very much for your time, I look forward to knowing your thoughts.
Kind regards,
Valerio
OS: Windows 10
MATLAB: 9.10.0.1851785 (R2021a) Update 6
PTB: 3.0.18 - Flavor: beta - Corresponds to SVN Revision 13009
The original post can be found in the Psychtoolbox forum.
KbQueueXXX functions: which to use and where do they go in relation to the trials' loop?
I'm trying to make a simple bot that would only react to messages with attachments. Then after a certain time, it would make a link to the message and send it on a moderation channel if it got 2 or more reactions.
#client.event
async def on_message(message):
if message.channel.id == 828579458167996420:
if message.attachments or "http" in message.content:
msgID = message.id
await message.add_reaction("<:uut:828580756384120912>")
await asyncio.sleep(200)
x = int
if message.reactions.count(x) >= 3:
link = 'https://discord.com/channels/11223345678900/828579458167996420/' + str(msgID)
channel = client.get_channel(892065611876823100)
x = x - 1
await channel.send("this post " + str(link) + "is liked " + str(x) + "times." )
the bot reacts to the messages i want, but it doesn't post anything in the moderation channel
I'm a beginner, sry for the messy codes :\
If you are not getting any kinds of errors, the issue lays right here, the if statement never gets triggered:
x = int
if message.reactions.count(x) >= 3:
message.reactions will return a list, your statement would count how many times x is in that list (which is always zero).
What you wanna do instead is just to get the total length of that list, like this:
if len(message.reactions) >= 3:
Also other small improvement tip: You can get the URL of a message with message.jump_url
EDIT:
Sorry, what you actually need to do to get the reaction count is to either search the emoji you want to count, or the shortcut would be to just get the first reaction in that list, since your bot should be the first to react to that message anyways:
#this will return a list of reactions that match the emoji you want
emoji = [x for x in message.reactions if str(x.emoji) == '😂'] #replace the emoji with the one you want
print(emoji[0].count) #counts the first (and only) emoji in the new list
"Shortcut" version, just gets the first reaction count:
print(message.reactions[0].count)
Trying to get bot to send a message when the person sends a message in range
async def on_message(message):
if 0 < Message < 100 in message.content:
await bot.message.send(message.channel, "you are in Bronze 1")
Ok first of all. Please don't code on mobile. Especially not python. That will mess things up way too much.
Second, please define your variables properly. Message is not a defined variable. It will return an error.
So, as you said, you wanted two ways to do this. The number should be the message itself, or within the message.
For the first example, all you have to do is cast the message.content to int. Then you can check if it's in the range.
if 0 < int(message.content) < 100:
...
For the second example, you will have to do something similar, however, you should split the entire message.content string and convert the number ones into integers. I'm assuming that the number will not be within a word and it will be by itself.
for word in message.content.split():
if word.isnumeric():
num = int(word)
if 0 < num < 100:
... # return and send message to avoid spamming
So, What I'm trying to do is make calls to a Reporting API to filter by all possible breakdowns (breakdown the reports by site, avertiser, ad type, campaign, etc...). But, one issue is that the breakdowns can be unique to each login.
Example:
user1: alice123's reporting breakdowns are ["site","advertiser","ad_type","campaign","line_items"]
user2: bob789's reporting breakdowns are ["campaign","position","line_items"]
When I first built the code for this reporting API, I only had one login to test with, so I hard coded the loops for the dimensions (["site","advertiser","ad_type","campaign","line_items"]). So what I did was pinged the API for a report by sites. Then for each site, pinged for advertisers, and each advertiser, I pinged for the next dimension and so on..., leaving me with a nested loop of ~6 layers.
basically what I'm doing:
sites = mechanize.get "#{base_ur}/report?dim=sites"
sites = Yajl::Parser.parse(sites.body) # json parser
sites.each do |site|
advertisers = mechanize.get "#{base_ur}/report?site=#{site.fetch("id")}&dim=advertiser"
advertisers = Yajl::Parser.parse(advertisers.body) # json parser
advertisers.each do |advertiser|
ad_types = mechanize.get "#{base_ur}/report?site=#{site.fetch("id")}&advertiser=#{advertiser.fetch("id")}&dim=ad_type"
ad_types = Yajl::Parser.parse(ad_types.body) # json parser
ad_types.each do |ad_type|
...and so on...
end
end
end
GET <api_url>/?dim=<dimension to breakdown>&site=<filter by site id>&advertiser=<filter by advertiser id>...etc...
At the end of the nested loop, I'm left with a report that's broken down as much granularity as possible.
This works now since I only thought that there was one path of breaking down, but apparently each account could have different dimensions breakdowns.
So what I'm asking is if given an array of breakdowns, how can I set up a nested loop to traverse down dynamically do the granularity singularity?
Thanks.
I'm not sure what your JSON/GET returns exactly but for a problem like this you would need recursion.
Something like this perhaps? It's not very elegant and can definitely be optimised further but should hopefully give you an idea.
some_hash = {:id=>"site-id", :body=>{:id=>"advertiser-id", :body=>{:id=>"ad_type-id", :body=>{:id=>"something-id"}}}}
#breakdowns = ["site", "advertiser", "ad_type", "something"]
def recursive(some_hash, str = nil, i = 0)
if #breakdowns[i+1].nil?
str += "#{#breakdowns[i]}=#{some_hash[:id]}"
else
str += "#{#breakdowns[i]}=#{some_hash[:id]}&dim=#{#breakdowns[i + 1]}"
end
p str
some_hash[:body].is_a?(Hash) ? recursive(some_hash[:body], str.gsub(/dim.*/, ''), i + 1) : return
end
recursive(some_hash, 'base-url/report?')
=> "base-url/report?site=site-id&dim=advertiser"
=> "base-url/report?site=site-id&advertiser=advertiser-id&dim=ad_type"
=> "base-url/report?site=site-id&advertiser=advertiser-id&ad_type=ad_type-id&dim=something"
=> "base-url/report?site=site-id&advertiser=advertiser-id&ad_type=ad_type-id&something=something-id"
If you are just looking to map your data, you can recursively map to a hash as another user pointed out. If you are actually looking to do something with this data while within the loop and want to dynamically recreate the loop structure you listed in your question (though I would advise coming up with a different solution), you can use metaprogramming as follows:
require 'active_support/inflector'
# Assume we are given an input of breakdowns
# I put 'testarr' in place of the operations you perform on each local variable
# for brevity and so you can see that the code works.
# You will have to modify to suit your needs
result = []
testarr = [1,2,3]
b = binding
breakdowns.each do |breakdown|
snippet = <<-END
eval("#{breakdown.pluralize} = testarr", b)
eval("#{breakdown.pluralize}", b).each do |#{breakdown}|
END
result << snippet
end
result << "end\n"*breakdowns.length
eval(result.join)
Note: This method is probably frowned upon, and as I've said I'm sure there are other methods of accomplishing what you are trying to do.
I'm trying to display total calls from a twilio object as well as unique calls.
The total calls is simple enough:
# set up a client to talk to the Twilio REST API
#sub_account_client = Twilio::REST::Client.new(#account_sid, #auth_token)
#subaccount = #sub_account_client.account
#calls = #subaccount.calls
#total_calls = #calls.list.count
However, I'm really struggling to figure out how to display unique calls (people sometimes call back form the same number and I only want to count calls from the same number once). I'm thinking this is a pretty simple method or two but I've burnt quite a few hours trying to figure it out (still a ruby noob).
Currently I've been working it in the console as follows:
#sub_account_client = Twilio::REST::Client.new(#account_sid, #auth_token)
#subaccount = #sub_account_client.account
#subaccount.calls.list({})each do |call|
#"from" returns the phone number that called
print call.from
end
This returns the following strings:
+13304833615+13304833615+13304833615+13304833615+13304567890+13304833615+13304833615+13304833615
There are only two unique numbers there so I'd like to be able to return '2' for this.
Calling class on that output shows strings. I've used "insert" to add a space then have done a split(" ") to turn them into arrays but the output is the following:
[+13304833615][+13304833615][+13304833615][+13304833615][+13304567890][+13304833615][+13304833615][+13304833615]
I can't call 'uniq' on that and I've tried to 'flatten' as well.
Please enlighten me! Thanks!
If what you have is a string that you want to manipulate the below works:
%{+13304833615+13304833615+13304833615+13304833615+13304567890+13304833615+13304833615+13304833615}.split("+").uniq.reject { |x| x.empty? }.count
=> 2
However this is more ideal:
#subaccount.calls.list({}).map(&:from).uniq.count
Can you build an array directly instead of converting it into a string first? Try something like this perhaps?
#calllist = []
#subaccount.calls.list({})each do |call|
#"from" returns the phone number that called
#calllist.push call.from
end
you should then be able to call uniq on #calllist to shorten it to the unique members.
Edit: What type of object is #subaccount.calls.list anyway?
uniq should work for creating a unique list of strings. I think you may be getting confused by other non-related things. You don't want .split, that's for turning a single string into an array of word strings (default splits by spaces). Which has turned each single number string, into an array containing only that number. You may also have been confused by performing your each call in the irb console, which will return the full array iterated on, even if your inner loop did the right thing. Try the following:
unique_numbers = #subaccount.calls.list({}).map {|call| call.from }.uniq
puts unique_numbers.inspect