Writing a bot for my forum
Hey guys,
I'm trying to write a plugin that can create a new Discussion, and might also make new posts.
Now I've lifted this code from vanilla\applications\vanilla\settings\stub.php, and it works sort of.
It creates the discussion, adds a comment to it. I can browse to those discussions by browsing to http://localhost/vanilla/index.php?p=/discussion/3/discussie#latest, for example.
But somehow it breaks my forum, and if I browse to http://localhost/vanilla/index.php?p=/categories/general, it doesn't show the different threads, only this message:
Debug Trace
Info delivery method: XHTML
Info delivery type: ALL
Info syndication: NONE
Anyone know what's up with that?
The code I use to create a new discussion and comment:
$DiscussionModel=new DiscussionModel();
$DiscussionTitle = "BAM! You’ve got a sweet forum";
$DiscussionBody = "Default message";
$CommentBody = "Default message";
$SQL=Gdn::sql();
// Prep content meta data
$SystemUserID = Gdn::userModel()->GetSystemUserID();
$Now = Gdn_Format::toDateTime();
$CategoryID = val('CategoryID', CategoryModel::DefaultCategory());
// Insert first discussion & comment
$DiscussionID = $SQL->Options('Ignore', true)->insert('Discussion', array(
'Name' => t('StubDiscussionTitle', $DiscussionTitle),
'Body' => t('StubDiscussionBody', $DiscussionBody),
'Format' => 'Html',
'CategoryID' => $CategoryID,
'ForeignID' => 'stub',
'InsertUserID' => $SystemUserID,
'DateInserted' => $Now,
'DateLastComment' => $Now,
'LastCommentUserID' => $SystemUserID,
'CountComments' => 1
));
$CommentID = $SQL->insert('Comment', array(
'DiscussionID' => $DiscussionID,
'Body' => t('StubCommentBody', $CommentBody),
'Format' => 'Html',
'InsertUserID' => $SystemUserID,
'DateInserted' => $Now
));
$SQL->update('Discussion')
->set('LastCommentID', $CommentID)
->where('DiscussionID', $DiscussionID)
->put();
$DiscussionModel->UpdateDiscussionCount($CategoryID);
$DiscussionModel->save($DiscussionData);
Comments
Apparently it didn't have anything to do with my code, now I created an extra category everything works as expected.
Yay!
Ignore the $DiscussionModel->save($DiscussionData); at the end by the way, that was a remainder of another draft of a comment I was going to place on here.
Do you know that there are already some bot plugins?
https://vanillaforums.org/addon/bot-plugin
https://github.com/vanilla/minion
I think the last one wouldn't work out of the box with the open source version of Vanilla since it relies quite often on a closed source plugin. But it could be a source of inspiration.
Instead of using SQL, I would advice to use the models own save methods:
You should take a look at both classes (class.discussionmodel.php and class.commentmodel.php) to see more information on what happens "behind the scenes" when you call that method. Looking at Vanillas source code is very insightful.
I'm keen in particular to know what you think of using Bot if you choose to look into it.
@R_J,
I tried using the models first. I wanted to write a bot that monitored a mailbox, and then post the information it received from certain senders to the forum. I wanted to use https://vanillaforums.org/addon/feeddiscussions-plugin as a basis for my own plugin. But I thought that I could put use the code to monitor the mailbox outside the plugin class, to excecute it every time the file was included instead of having to rely on handlers.
That produced some weird categorymodel error I couldn't figure out (because apparently you're not supposed to do that), which is why I tried using SQL queries directly (because I was more confident I knew what was going on). When I figured out the weird error was due to my code being executed at a wrong time, I didn't think of switching it back from direct SQL to models again.
But thanks for the snippet of code, I now at least have a working example of how it's supposed to be done!
And thanks for the link to the two bots!
@Linc, I'm afraid my bot will be less interactive than Bot seems to be judging from its source code. I'll keep you updated however if I decide to use it!
Wrap your code in a public function pluginController_yourPluginName_create($sender) {}
Use a config setting which stores the time of the last check.
Let the first lines in your code be a check if now - that config setting is > 5 minutes
Create a cron job that calls yourforum.com/plugin/yourpluginname periodically
Hey R_J, I was using Base_Render_Before(), since that seemed to fire every time the forum was loaded.
How do you set config settings with Vanilla? I wrote my own code to store values, because I couldn't figure out how to store permanent variables with standard functions.
Anyway, I'm getting a white screen. Any more helpful pointers would be very much appreciated
function startChecking() {
//Here I check wether it's been long enough to check again
if (!$this->needsChecking()) {
return;
}
$DiscussionModel = new DiscussionModel();
//If discussionID == false, there's no discussion yet so one should be created
if (!$DiscussionID) {
$discussion = array(
'Name' => 'Hello From Robot',
'Format' => c('Garden.InputFormatter'),
'CategoryID' => $CategoryID,
'Body' => 'Call me HAL',
'InsertUserID' => $SystemUserID,
'DateInserted' => Gdn_Format::toDate()
);
$DiscussionID = $DiscussionModel->save($discussion);
$this->setValueInDB("DiscussionID", $DiscussionID);
}
//Username and password of my gmail account
$username = "";
$password = "";
$mailbox = '{imap.gmail.com:993/ssl/novalidate-cert}INBOX';
$mbox = imap_open($mailbox, $username, $password);
$messageUID = $this->getValueFromDB('LastUIDRead');
//If there haven't been messages been imported before start with the first
$startIndex = $messageUID ? $messageUID : 1;
$num_msgs = imap_num_msg($mbox);
//Import a max of 10 messages each time to make use you don't use too much execution time
for ($i = $startIndex; $i <= $num_msgs && $i < $startIndex + 10; $i++) {
$commentBody = imap_body($mbox, $i);
$commentHeader = imap_headerinfo($mbox, $i);
//For testing purposes import even messages, real thing will import based on sender etc.
if ($i % 2 === 0) {
/* //If I comment this part out it works
$comment = array(
'DiscussionID' => $DiscussionID,
'Body' => t('StubCommentBody', "[b]$i" . "[/b]
This is the $i th message"),
'Format' => 'Html',
'InsertUserID' => $SystemUserID,
'DateInserted' => Gdn_Format::toDate()
);
$commentModel = new CommentModel();
$commentID = $commentModel->save($comment);
$commentModel->save2($commentID, true);
*/
}
}
//Set LastUIDRead to the last UID read
$this->setValueInDB('LastUIDRead', imap_uid($mbox, $i));
$DiscussionModel->UpdateDiscussionCount($CategoryID);
imap_close($mbox);
}
Okay, apparently as long as I limit it to a single comment getting posted per check it works.
I've disabled spamcheck ($commentModel->SpamCheck=false;), is there another flood protection in place I've missed?
You can post source code, if you enclose the code with a 3 times "~" in a single row like that:
~~~
. That makes it easier to read your code.You can write to config like that
saveToConfig($key, $value);
and retrieve it like that$value = c($key);
. Look at /library/core/functions.general.php to find some useful functions.There is a naming convention for $key. It should be "YourPluginsName.SomeInformativeName". if your plugin is simply called "bot", the config keys name could be e.g. "bot.TimestampLastChecked"
That's a great finding! I would have messed with changing the config values temporarily to fool the spam check, but setting that property is way more elegant!
I do not know anything about spam protection. I've done a quick search for "flood" in the source code but found nothing of interest...
Would you mind sharing your
setValueInDB
andgetValueFromDB
? If you are not yet experienced with Vanilla there might be room for improvement.And you should think about making your mail credentials also config settings. That way you would be able to share your plugin without having to fear that you expose your mail accounts credentials.
Thanks R_J!
Figured out the error: php execution time was exceeded >_> sorry for wasting your time on something that had nothing to do with the code! I've introduced a hard limit and now it works perfectly.
This was the code for my getter and setter by the way, thanks for showing me the correct way:
I didn't understood they were your config functions replacements, now I see.
Although it is not important anymore, here's one note on your setValueInDB: the sql class provides a
replace($Table = '', $Set = null, $Where, $CheckExisting = false) {}
method which does exactly what you do: check if a key exists, update if yes, insert if not. It just looks cleaner if you use it:Hi @Caylus, could you post your entire plugin once it's finished?
@R_J Thanks! That's going to be useful for other plugins!
@rbrahmson, sure! It's not really user friendly though, I couldn't figure out how to make a settings page.
I do get a settings button, but when I click on it I go to http://localhost/vanilla/index.php?p=/settings/bot and get a page not found error (because I can't figure out in which directory to put bot.php).
You might need to enable pretty urls
Create a views folder for your settings page inside your plugin where you will add the forms to input the values or options.
Look at my Copyright plugin for a simple input form and how to make the settings page. Or even the MagpieFeeds you liked…
❌ ✊ ♥. ¸. ••. ¸♥¸. ••. ¸♥ ✊ ❌
settingsurl is required
depending on controller and how you set things up.
'SettingsUrl' => '/plugin/yourpluginname',
or
'SettingsUrl' => '/dashboard/settings/tagging',
you can add a settings page in views folder, some people like to use a settings.php in the views folder and some like to avoid it. personal preference.
here is an example without a specific settings view
https://vanillaforums.org/discussion/25253/simple-setting-screens-with-configurationmodule
here is a file in views folder. (edit:just noticed after posting above comment has an example for a file in views folder)
https://github.com/vanilla/addons/blob/master/plugins/Voting/views/settings.php
https://github.com/vanilla/addons/blob/master/plugins/Voting/class.voting.plugin.php#L20
https://github.com/vanilla/addons/blob/master/plugins/Voting/class.voting.plugin.php#L46
Pragmatism is all I have to offer. Avoiding the sidelines and providing centerline pro-tips.
Thanks guys! Got it to work!
It is customary to post what you did exactly to fix or what was the problem. This way people other than you might get help too
You might even earn the Solution Sayer Badge …
❌ ✊ ♥. ¸. ••. ¸♥¸. ••. ¸♥ ✊ ❌
he could just add the plugin to the plugin folder (if you want to support) or zip it up here and add it as an attachment, then you are not as obliged to support.
regarding your fix to feeddiscussions...
under the plugin is even better - helps current users.
https://vanillaforums.org/addon/feeddiscussions-plugin
choose ask question. change category to feedback and post your change.
then people can test and then a moderator or anyone else including you can add it to github.
Pragmatism is all I have to offer. Avoiding the sidelines and providing centerline pro-tips.
Yea, but his fix was for the Magpie and learning how to make a settings page or not having enabled pretty url as the cause and solution can help give closure to this particular discussion.
❌ ✊ ♥. ¸. ••. ¸♥¸. ••. ¸♥ ✊ ❌
oh thought it was feed discussions plugin.
Pragmatism is all I have to offer. Avoiding the sidelines and providing centerline pro-tips.