This is my first time using this site so I apologize if the formatting's sub par.
The problem:
My Discord bot (javascript) recently has stopped responding when #mentioned. There were no changes to the code to cause this and it was working perfectly fine not too long ago. A friend who's bot is programmed similarly also has this issue so I know that it's not only me.
The bot's basically a chat-and-reply bot; you #mention it's name and include a trigger and it has a random chance to respond with one of four responses. However, something's happened where it doesn't seem to register that it's been #mentioned and therefore doesn't reply.
So, for example, if I were to type "#bot hi!" in discord, the bot would reply with one of the following replies: "It's too early for this.", "Mornin'.", "I need coffee.". "[yawn, mumbled greeting]".
I've tried replacing client.user.toString() directly with it's client identifier as well as the identifiers that would be used in discord (for example; "#name#0000", "<#########>") but those are also ignored. I've added an arrow next to this area in the code.
I'm not sure if there was an update that's made some of the code go out of date, but I've tried searching for similar issues with no success.
I'm relatively sure that the issue isn't with the processChat(receivedMessage) function, as I can replace the noted problem section with an alternate trigger such as:
if (receivedMessage.content.startsWith("#all ")) {
processChat(receivedMessage)
}
and the bot will send a reply. It simply doesn't seem to want to respond when mentioned; the rest of the code works as it should. While this is something I can convert to, I've had this bot on my server for almost a year now and there are multiple other server members who'd need to adapt to the change. I'd rather get things running the way they used to than have everyone fall out of habit to compensate.
Is there a way to fix this?
Here's a small example code that has the same issue:
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log("Connected as " + client.user.tag);
})
//The color for all of the bot's messages
messageColor = 4611141;
client.on('message', receivedMessage => {
// Prevent bot from responding to its own messages
if (receivedMessage.author == client.user) {
return;
}
//process regular triggers (# mentions)
//This is where the issue seems to be <--------------------------
if ((receivedMessage.content.includes(client.user.toString()))) {
processChat(receivedMessage);
}
});
//For usage with chat prompts and triggers
function processChat(receivedMessage) {
if ((receivedMessage.content.toLowerCase().includes("hi"))){
var random = randomInt(5, 1) ;
if (random == 1) {
receivedMessage.channel.send({embed: {
color: messageColor,
description: "It's too early for this."
}});
} else if (random == 2) {
receivedMessage.channel.send({embed: {
color: messageColor,
description: "Mornin'."
}});
} else if (random == 3) {
receivedMessage.channel.send({embed: {
color: messageColor,
description: "I need coffee."
}});
} else if (random == 4) {
receivedMessage.channel.send({embed: {
color: messageColor,
description: "*[yawn, mumbled greeting]*"
}});
} else {
return;
}
}
}
//Random number generation
function randomInt(max, min) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
client.on('error', console.error);
client.login(token)
Switching from
if ((receivedMessage.content.includes(client.user.toString()))) {
processChat(receivedMessage);
}
to
if (recievedMessage.isMemberMentioned(client.user)) {
processChat(recievedMessage)
}
Solved the problem. Thanks, Bauke, for those links.
Related
I'm using this library and i can connect without problems.
Usually when I have worked with sockets the code that ever i used is:
socket = io.connect(url, { query: ‘token=’ + token});
and i can see this info reading socket.request._query
Using socket.io for react native i'm trying to send params:
this.socket = new SocketIO('http://localhost:3000', { query: ‘token=’ + token});
but in socket.request._query only can see this log:
{ transport: 'polling', b64: '1' }
In the library some options are mentioned like: connectParams. But i don't know how i can see the info
Related: link
It's not pretty detailed in the repo, but connectParams is a key/value object, and furthermore the values you sent in it will be appended in the url, as shown here:
if connectParams != nil {
for (key, value) in connectParams! {
let keyEsc = key.urlEncode()!
let valueEsc = "\(value)".urlEncode()!
queryString += "&\(keyEsc)=\(valueEsc)"
}
}
>Source<
So, you should try using connectParams like this(though I'm not sure how you tried it before):
this.socket = new SocketIO('http://localhost:3000', {
connectParams: {
myAwesomeQueryStringParam: "someRandomValue"
}
});
PS: forgive me, my english is pretty bad
I have a meteor app where I want to let the user click buttons to switch between 2 different filtered views of the data, say "chart" records with a status of 10 or 11. Some users of the app might never want to see status 10, while others might never want to see status 11.
I'm trying to find a way through publish/ subscribe to work this out most efficiently... not pulling up records someone doesn't want to see, while also reducing network traffic.
First idea was the following... a publish on the server with a parameter of the status:
Meteor.publish("charts1", function (status) {
console.log('someone subscribed to my publish of charts.. returning all charts now of status ' + status + " max 500 though");
return Chart.find({"chartStatus": status}, {limit: 500, sort: {"demographics.lastName": 1}});
});
then, on client, I have a template helper:
chartsOnClient: function () {
return Chart.find({"chartStatus":Session.get('currentStatusFilter')}, {sort: {"demographics.lastName": 1}});
}
and 2 buttons which, when clicked will set the session filter and subscribe at the same time:
Template.addChartForm.events = {
'click #mybutton10': function () {
console.log("subscribing to to status 10...");
Session.set('currentStatusFilter', 10);
Meteor.subscribe('charts1', 10);
},
'click #mybutton11': function () {
console.log("subscribing to status 11...");
Session.set('currentStatusFilter', 11);
Meteor.subscribe('charts1', 11);
},
}
and, of course, a template that iterated over "chartsOnClient" to display the records.
This worked... when I clicked button10, I got status 10 in my template, and clicking button11 gave me status 11 in my template...And, the added benefit was that if I never clicked button11, my local client only held the records of status 10, and vice versa, and clicking both buttons would fill up my local collection with the union of both sets.
The only issue I see with this method is that every click of the buttons results in the server message "someone subscribed to my publish of charts..."... indicating that the client is talking to the server and running the find method.
I suspect this is not ideal... for, if I wanted, I could move the 2 "subscribe" calls outside of the click events, like this:
Meteor.subscribe('charts1', 10);
Meteor.subscribe('charts1', 11);
Template.addChartForm.events = {
'click #mybutton10': function () {
console.log("subscribing to to status 10...");
Session.set('currentStatusFilter', 10);
},
'click #mybutton11': function () {
console.log("subscribing to status 11...");
Session.set('currentStatusFilter', 11);
},
}
and when I do this, I get the same end user experience, however, the server console only shows the "someone subscribed to my publish"... message once upon startup, instead of every time the buttons are clicked.
The downside is that both sets of records are pulled up to the client for each user, even users that might never click on both buttons. But the upside is that the subscribe method is not called each time they click to switch between views of the data...
Is there something I'm not understanding here? Which method is best? is there a better way to do this altogether? I'm new to meteor and mongo.
Thank you.
Edit, based on #mattk , I am going to do the subscribes in the button clicks, but use an array in a session variable to prevent a second subscription if I've already subscribed with that particular filter:
'click #mybutton10': function () {
Session.set('currentStatusFilter', 10);
var filtersAppliedSoFar = Session.get('filtersAppliedSoFar');
if (filtersAppliedSoFar.indexOf(10) == -1) {
console.log("subscribing to to status 10...");
filtersAppliedSoFar.push(10);
Session.set('filtersAppliedSoFar', filtersAppliedSoFar);
Meteor.subscribe('charts1', 10);
}
},
'click #mybutton11': function () {
Session.set('currentStatusFilter', 11);
var filtersAppliedSoFar = Session.get('filtersAppliedSoFar');
if (filtersAppliedSoFar.indexOf(11) == -1) {
console.log("subscribing to status 11...");
filtersAppliedSoFar.push(11);
Session.set('filtersAppliedSoFar', filtersAppliedSoFar);
Meteor.subscribe('charts1', 11);
}
},
This way, I don't pull up the data until the user clicks on the particular filter, but I also don't resubscribe if they click back and forth between the 2 filters, which is expected.
EDIT AGAIN: after asking this question How do you securely log out and clear all subscriptions? and getting directed toward Subscription Manager https://github.com/meteorhacks/subs-manager I have discovered that Subscription manager achieves what I was looking for here: I didn't want it to hit the server a second time if my client called .subscribe again. Instead of using a session variable (filtersAppliedSoFar) to know if the client has already subscribed, the subs manager keeps track of this automatically...I just call .subscribe on the subscription manager object and it won't hit the server the second time. .. then the added benefit is I can call .clear() when logging out and all subscriptions are stopped.
There's nothing wrong with your first pattern. Every time you talk to the server, you want to ask it or tell it something new, and that's exactly what you're doing: you're asking it for ONLY the data you need. If you want to reduce bandwidth, limit the fields returned; chances are you aren't going to need every field that is stored in the doc.
I follow something like this:
//Client code (event click)
Meteor.subscribe('patients',
{"chartStatus":Session.get('currentStatusFilter')},
{
fields: {'demographics': 1, 'chartStatus': 1},
sort: {"demographics.lastName": 1}
});
//Server code
Meteor.publish('patients', function(query, options) {
if (options) {
check(options, {
sort: Match.Optional(Object),
limit: Match.Optional(Number),
fields: Match.Optional(Object)
});
}
return Patients.find(query,options);
});
Note that the client can now ask for whatever fields she wants. If in the future there are certain fields in the doc that shouldn't be sent over the wire, you need to do a permission check, too. Right now, this doesn't seem like a problem for you since you've been sending over the entire doc.
I previously asked a similar question about ejabberd, however ejabberd was giving other problems, so I switched to openfire. For the sake of not making the original qestion to cluttered, I decided to create a new question, since this question pertains to openfire and is a different issue than the one I was having with ejabberd.
So, here goes the question:
I have a strophe.js xmpp client, which connects to an openfire 3.10.0 alpha server running on the Amazon cloud. I need 3.10.0 alpha over 3.9.3 because of a bfix which is included in the former, but not the latter.Anyway, since this is a strophe client, I have enabled bosh, and I can see it running at myAWSDNS.com:7070. I am able to connect to the server via my client using this bosh service and existing accounts, and send messages back and forth so it seems to be functioning ok.
I would also like to add in-band registration, for which I use strophe.register.js
This is the code I use for this:
var tempConn = new Strophe.Connection("http//myAWSDNS.com:7070/http-bind/");
tempConn.register.connect("myAWSDNS.com", function (status) {
if (status === Strophe.Status.REGISTER) {
// fill out the fields
connection.register.fields.username = "juliet";
connection.register.fields.password = "R0m30";
// calling submit will continue the registration process
connection.register.submit();
} else if (status === Strophe.Status.REGISTERED) {
console.log("registered!");
// calling login will authenticate the registered JID.
connection.authenticate();
} else if (status === Strophe.Status.CONFLICT) {
console.log("Contact already existed!");
} else if (status === Strophe.Status.NOTACCEPTABLE) {
console.log("Registration form not properly filled out.")
} else if (status === Strophe.Status.REGIFAIL) {
console.log("The Server does not support In-Band Registration")
} else if (status === Strophe.Status.CONNECTED) {
// do something after successful authentication
} else {
// Do other stuff
}
});
This seems to work fine, as it enters the first if-bracket (status === Strophe.Status.REGISTER), and tries to set connection.register.fields.username = "juliet";
However, here, when executing that line, it jumps into strophe.js line 2476:
if (this.connect_callback) {
try {
this.connect_callback(status, condition);
} catch (err) {
Strophe.error("User connection callback caused an " +
"exception: " + err);
}
}
where 2476 is the code in the catch(err) { ...... } bracket.
If I inspect err, this is what I get:
So message: connection is not defined and, obviously, the regstration doesnt work, and I am not sure why. Does anyone have any input on this?
Thanks, and best regards,
Chris
You might not like this answer... The reason for connection == undefined is because you named it tempConn.
It's my intention to get all past posts. I have created a Facebook app. When this code is executed, I have properly logged in with the correct appid and secret.
function loadPage() { // calls the first batch of records
FB.api("/me/feed",{},function(response) { procBatch(response) } );
}
function procBatch(dat) { // handle this batch, request the next batch
for ( i = 0; i < dat.data.length; i++ ) {
procRow(dat.data[i]); // process this row
}
if ( typeof(dat.paging) != 'undefined' ) {
FB.api(dat.paging.next, {}, function(response){ procBatch(dat); } );
} else {
alert("No more records expected");
}
}
Unfortunately, this only takes me back about six months.
Is it possible to go back further?
Thanks in advance.
Yes, that's possible.
This can be done by increasing the limit of number object per page. I've not tried retrieving ALL the posts, but I am pretty sure that you can easily retrieve posts which are even few years old.
You can call /me/feed?limit=numberOfObjectsPerPage instead of just /me/feed to increase this limit.
You can find more information on this here. Do have a look at the Time Based Pagination on the link. This will explain you how to manage the output for the feeds.
I have an abstraction on the iOS Keychain API that seems to work well. Basically, it has:
public string GetGenericPasswordField(string account)
{
var record = SecKeyChain.QueryAsRecord(query, out code);
if (code == SecStatusCode.ItemNotFound)
return null;
return NSString.FromData(record.ValueData, NSStringEncoding.UTF8);
}
public void SetGenericPasswordField(string account, string value)
{
if (value == null)
{
SecKeyChain.Remove(record);
return;
}
var record = new SecRecord (SecKind.GenericPassword) {
Service = service,
Label = label,
Account = account,
ValueData = NSData.FromString (value),
};
SecStatusCode code = SecKeyChain.Add (record);
if (code == SecStatusCode.DuplicateItem)
{
// (remove and re-add item)
}
}
I have used this abstraction on my app's settings screen to save values while leaving, and then load those values elsewhere in the app.
But I've run into an issue where saving a value does not appear to take effect if you don't leave the current ViewController. What I'm doing is analogous to:
if (Keychain.GetGenericPasswordField("RemoteLogin") == null)
{
var remotePassword = GetFromDialog();
Keychain.SetGenericPasswordField("RemoteLogin", Hash(remotePassword));
// Safe to assume the RemoteLogin password got saved, right?
}
// Later on...
if (Keychain.GetGenericPasswordField("RemoteLogin") == null)
{
// This block is being executed
}
I've stepped through the code in the debugger to confirm that things are as I'm describing them, and that my abstraction method really is getting a SecStatusCode.ItemNotFound back, meaning null is the appropriate value to return.
I worked around this once by moving the first half of my code back to a previous ViewController, and for some reason that seemed to wake it up, or clear out whatever caching is taking place. But now I've encountered another situation where that's not really practical.
Why is this happening? Is my abstraction leaking?