phpbb_ailabs icon indicating copy to clipboard operation
phpbb_ailabs copied to clipboard

I noticed that these bots can’t seem to mention each other properly

Open hbproph opened this issue 8 months ago • 5 comments

I’m currently experimenting with a few AI bots on my board — like [mention]HelperBot[/mention], [mention]ForumGuide[/mention], and [mention]QuickFixer[/mention]. Everything works great, but I noticed that these bots can’t seem to mention each other properly. Even when using the [mention]Name[/mention] BBCode format in their replies, it doesn't trigger a proper mention or notification like it does for human users.

Is there any way to enable or support bot-to-bot mentions within the extension? Or perhaps a workaround you’d recommend?

Thanks again for all the work you’ve put into AILabs — it’s truly top-tier.

hbproph avatar Jun 14 '25 14:06 hbproph

You're correct I was playing with a similar concept where one bot can communicate with another via @mention. Unfortunately this is not going to work and some code changes will be required. I did not look too far into that.

traverse-in-reverse avatar Jun 14 '25 18:06 traverse-in-reverse

Thank you for your reply.

My goal was to create conversations between bots and have them respond randomly to forum posts.

Unfortunately, it seems this isn’t possible.

I spent an entire day experimenting with the event listener from the Simple Mentions extension, trying to find a workaround. I attempted to hook into the event like this:

'core.modify_text_for_storage_before' => 'convert_mentions',

Using the following code:

/**

  • Convert @bot-style mentions into BBCode mentions. */ public function convert_mentions($event) { if (isset($event['text'])) { $text = $event['text'];

     $text = preg_replace('/@EVE\b/', '[mention]EVE[/mention]', $text);
     $text = preg_replace('/@Donna\b/', '[mention]Donna[/mention]', $text);
     $text = preg_replace('/@Theresa\b/', '[mention]Theresa[/mention]', $text);
    
     $event['text'] = $text;
    

    } }

However, even when AILab correctly inserts a [mention]...[/mention] tag, it never actually registers as a proper mention in phpBB.

I tried several approaches, but no matter what, phpBB always stores the output as plain text. So far, I haven’t found a way around this.

hbproph avatar Jun 14 '25 19:06 hbproph

It's been a while since I looked at that code. If I recall correctly the problem was that the event has to be triggered by HTTP GET/POST. When you modify post and/or insert a mention/new post within the event handler it will not trigger the correct sequence of events to handle that action. There's probably a way to trigger event processing for inserted/updated posts but it was a bit too much for me at the time to process so I just gave up. Not sure if that will help you.

traverse-in-reverse avatar Jun 14 '25 20:06 traverse-in-reverse

Actually yes — thank you, that really help,

So from what I understand, the issue is that when the code inserts posts directly, it bypasses phpBB’s normal submission flow. That means the mention system doesn’t get triggered at all, because it relies on a proper HTTP request to fire the full chain of events.

I’m now thinking about a few possible solutions. One idea is to try forcing a re-parse of the post after it’s been inserted — maybe by calling generate_text_for_storage() manually and enabling flags like allow_bbcode, allow_smilies, etc.

Another option I’m considering is switching to use phpBB’s native submit_post() function instead of inserting into the database directly. That should take care of everything — mentions, notifications, BBCode parsing, and cache updates — since it follows the full internal process.

If neither of those works cleanly, I might even try simulating a POST request internally using something like request->enable_super_globals() to see if that kicks the system into action.

Thanks again for pointing me in the right direction this gives me a clearer path to test next

hbproph avatar Jun 16 '25 11:06 hbproph

This is how far i get unfortunately mention is still stored as a text and never get praised, I tried everything. First I just added [mention]Donna[/mention] — didn’t work. Then I switched to using submit_post() with all the right BBCode flags — still no praise. After digging into the mention extension, I realized it needs a real user session, so I injected the bot’s identity into $user->data. The posts got submitted properly, but mentions still weren’t praised.

Turns out the extension expects the mention to come from the JS dropdown — that’s how it links the username to a user ID. Without that, it won’t convert it. So I’m close, but the last missing step is manually converting [mention]Name[/mention] to [MENTION uid=X]Name[/MENTION]. That should finally make it work.

If you have any tip please share, thank you in advance.

<?php

/**
 *
 * AI Labs extension
 *
 * @copyright (c) 2023, privet.fun, https://privet.fun
 * @license GNU General Public License, version 2 (GPL-2.0)
 *
 */

namespace privet\ailabs\controller;

use privet\ailabs\includes\GenericCurl;
use Symfony\Component\HttpFoundation\JsonResponse;
use privet\ailabs\includes\AIController;
use privet\ailabs\includes\resultParse;

class chatgpt extends AIController
{
    protected $max_tokens = 4096;
    protected $settings_override;

    protected function process()
    {
        global $phpbb_root_path, $phpEx, $user, $auth, $db, $config;
        global $request, $phpbb_container;

        $this->job['status'] = 'exec';

        $this->job_update([
            'status' => $this->job['status'],
            'log' => json_encode($this->log)
        ]);
        $this->post_update($this->job);

        if (!empty($this->cfg->max_tokens)) {
            $this->max_tokens = (int)$this->cfg->max_tokens;
        }

        $prefix_tokens = empty($this->cfg->prefix_tokens) ? 0 : $this->cfg->prefix_tokens;

        $api_key = $this->cfg->api_key;
        $this->cfg->api_key = null;

        $api = new GenericCurl($api_key);
        $api->debug = $this->debug;

        $this->job['status'] = 'fail';
        $response = $this->language->lang('AILABS_ERROR_CHECK_LOGS');

        $api_response = null;

        $info = null;
        $content = [];
        $post_first_taken = null;
        $post_first_discarded = null;

        $history = $this->retrieve_history($this->max_tokens);
        $history_count = 0;
        $history_tokens = 0;

        $this->log['history'] = $history;
        $this->log_flush();

        if (!empty($history)) {
            foreach ($history as $key => $value) {
                if ($value['discard']) {
                    $post_first_discarded = $value['postid'];
                    break;
                }

                $history_count++;
                $post_first_taken = $value['postid'];
                $history_tokens += ($value['request_tokens'] + $value['response_tokens']);

                array_unshift(
                    $content,
                    ['role' => 'user', 'content' => $value['request']],
                    ['role' => 'assistant', 'content' => $value['response']]
                );
            }
        }

        $request_text = trim($this->job['request']);
        $configuration = ['temperature' => (float)$this->cfg->temperature];

        if ($this->extract_numeric_settings($request_text, ['temperature' => 'temperature'], $configuration, $this->settings_override)) {
            $this->log['settings_override'] = $this->settings_override;
        }

        $content[] = ['role' => 'user', 'content' => $request_text];

        if (!empty($this->cfg->prefix)) {
            array_unshift($content, ['role' => 'system', 'content' => $this->cfg->prefix]);
        }

        $request_json = [
            'model' => $this->cfg->model,
            'messages' => $content,
            'temperature' => (float)$configuration["temperature"],
            'frequency_penalty' => (float)$this->cfg->frequency_penalty,
            'presence_penalty' => (float)$this->cfg->presence_penalty,
        ];

        $this->log['request.json'] = $request_json;
        $this->log_flush();

        $request_tokens = 0;
        $response_tokens = 0;

        try {
            $api_result = $api->sendRequest($this->cfg->url_chat, 'POST', $request_json);
            $json = json_decode($api_result);
            $this->log['response'] = $json;
            $this->log['response.codes'] = $api->responseCodes;
            $this->log_flush();

            if (!empty($json->object) && !empty($json->choices) && strpos($json->object, 'chat.completion') !== false && in_array(200, $api->responseCodes)) {
                $this->job['status'] = 'ok';
                $api_response = $json->choices[0]->message->content;
                $response = $api_response;
                $request_tokens = $json->usage->prompt_tokens;
                $response_tokens = $json->usage->completion_tokens;
                if ($history_tokens > 0 || $prefix_tokens > 0) {
                    $this->log['request.tokens.raw'] = $request_tokens;
                    $request_tokens = $request_tokens - $history_tokens - $prefix_tokens;
                    $this->log['request.tokens.adjusted'] = $request_tokens;
                }
            }
        } catch (\Exception $e) {
            $this->log['exception'] = $e->getMessage();
            $this->log_flush();
        }

        $this->log['finish'] = date('Y-m-d H:i:s');

        if ($history_count > 0) {
            $viewtopic = "{$this->root_path}viewtopic.{$this->php_ext}";
            $discarded = '';
            if ($post_first_discarded != null) {
                $discarded = $this->language->lang('AILABS_POSTS_DISCARDED', $viewtopic, $post_first_discarded);
            }
            $total_posts_count = $history_count * 2 + 2;
            $total_tokens_used_count = $history_tokens + $request_tokens + $response_tokens;
            $info = $this->language->lang(
                'AILABS_DISCARDED_INFO',
                $viewtopic,
                $post_first_taken,
                $total_posts_count,
                $discarded,
                $total_tokens_used_count,
                $this->max_tokens
            );
        }

        $resultParse = new resultParse();
        $resultParse->message = $response;
        $resultParse->info = $info;
        $resultParse->settings = empty($this->settings_override) ? $this->settings_override : $this->language->lang('AILABS_SETTINGS_OVERRIDE', $this->settings_override);

        $final_response = $this->replace_vars($this->job, $resultParse);

        //  Submit post using phpBB's native submit_post() to trigger BBCode parsing + mentions
        include_once($phpbb_root_path . 'includes/functions_posting.' . $phpEx);

        $data = [
            'forum_id'      => (int)$this->job['forum_id'],
            'topic_id'      => (int)$this->job['topic_id'],
            'poster_id'     => (int)$this->job['poster_id'],
            'icon_id'       => false,
            'enable_bbcode' => true,
            'enable_smilies'=> true,
            'enable_urls'   => true,
            'enable_sig'    => true,
            'message'       => $final_response,
            'message_md5'   => md5($final_response),
            'bbcode_bitfield' => '',
            'bbcode_uid'    => '',
            'post_edit_locked' => 0,
            'topic_title'   => '',
            'notify_set'    => false,
            'notify'        => false,
            'post_time'     => time(),
            'forum_name'    => '',
            'enable_indexing' => true,
        ];

        $poll = $uid = $bitfield = $options = '';
        generate_text_for_storage($data['message'], $uid, $bitfield, $options, true, true, true);
        $data['bbcode_uid'] = $uid;
        $data['bbcode_bitfield'] = $bitfield;

        $post_id = submit_post('reply', $data['topic_title'], '', POST_NORMAL, $poll, $data);
        $this->job['response_post_id'] = $post_id;
        $this->job['response_time'] = time();

        $this->job_update([
            'status' => $this->job['status'],
            'attempts' => $this->job['attempts'] + 1,
            'response_time' => $this->job['response_time'],
            'response' => utf8_encode_ucr($api_response),
            'request_tokens' => $request_tokens,
            'response_post_id' => $this->job['response_post_id'],
            'response_tokens' => $response_tokens,
            'log' => json_encode($this->log)
        ]);

        $this->post_update($this->job);

        return new JsonResponse($this->log);
    }
}

hbproph avatar Jun 16 '25 19:06 hbproph