Abfrage wieviele PN's an den gleichen Empfänger

In diesem Forum kann man Fragen zur Programmierung stellen, die bei der Entwicklung von Mods für phpBB 3.0.x oder dem Modifizieren des eigenen Forums auftauchen.
Forumsregeln
phpBB 3.0 hat das Ende seiner Lebenszeit überschritten
phpBB 3.0 wird nicht mehr aktiv unterstützt. Insbesondere werden - auch bei Sicherheitslücken - keine Patches mehr bereitgestellt. Der Einsatz von phpBB 3.0 erfolgt daher auf eigene Gefahr. Wir empfehlen einen Umstieg auf die neuste phpBB-Version, welches aktiv weiterentwickelt wird und für welches regelmäßig Updates zur Verfügung gestellt werden.
Helmut
Mitglied
Beiträge: 2048
Registriert: 27.12.2002 20:35
Wohnort: Augsburg

Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Helmut »

Hallo,

ich bastle gerade eine Funktion mit der ich den Missbrauch der PN's als Privatchat verhindern kann. Dabei darf der User nur eine bestimmte Anzahl an PN's pro Stunde an den gleichen Empfänger schicken, dann wird die Absendung verhindert und eine Warnmeldung bekommt der User zu sehen.

Mein Problem ist jetzt wie die SQL Abfrage aussehen muss, damit ich die Empfänger user_id bekomme, welche der Absender am häufigsten kontaktierte.

Bis jetzt sieht meine Abfrage so aus:

Code: Alles auswählen

        $pm_count_time_limit2 = time() - 3600;

        $sql = 'SELECT p.msg_id, t.author_id, p.message_time, t.msg_id, t.user_id
            FROM ' . PRIVMSGS_TABLE . ' p
            LEFT JOIN ' . PRIVMSGS_TO_TABLE . ' t ON (p.msg_id = t.msg_id)            
                WHERE t.author_id =  ' . $user->data['user_id'] . '
                    AND t.user_id != t.author_id
                    AND p.message_time >= ' . $pm_count_time_limit2 . '
                    AND (SELECT COUNT(t2.user_id), t2.user_id, t2.author_id
                        FROM ' . PRIVMSGS_TO_TABLE . ' t2)
                    GROUP BY t2.user_id, p.message_time DESC';
                    
        $result = $db->sql_query($sql);
Ich bekomme aber den folgenden Fehler:
Allgemeiner Fehler
SQL ERROR [ mysql4 ]

Operand should contain 1 column(s) [1241]

SQL

SELECT p.msg_id, t.author_id, p.message_time, t.msg_id, t.user_id FROM phpbb3_privmsgs p LEFT JOIN phpbb3_privmsgs_to t ON (p.msg_id = t.msg_id) WHERE t.author_id = 4256 AND t.user_id != t.author_id AND p.message_time >= 1381061774 AND (SELECT COUNT(t2.user_id), t2.user_id, t2.author_id FROM phpbb3_privmsgs_to t2) GROUP BY t2.user_id, p.message_time DESC

BACKTRACE

FILE: (not given by php)
LINE: (not given by php)
CALL: msg_handler()

FILE: [ROOT]/includes/db/dbal.php
LINE: 757
CALL: trigger_error()

FILE: [ROOT]/includes/db/mysql.php
LINE: 193
CALL: dbal->sql_error()

FILE: [ROOT]/test4.php
LINE: 87
CALL: dbal_mysql->sql_query()
Wie muss die Abfrage aussehen, damit ich die user_id bekomme, welche am meisten PN's innerhalb einer Stunde an den gleichen Empfänger geschickt hat?

Gruß Helmut
Ich bin nicht ganz dicht.... na und.
Benutzeravatar
Miriam
Mitglied
Beiträge: 12310
Registriert: 13.10.2004 07:18
Kontaktdaten:

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Miriam »

Sieht ja kompliziert aus.
Was hältst Du von diesem Ansatz? ->

Code: Alles auswählen

SELECT COUNT(msg_id) > 4 AS private_messages, author_id, to_address, bcc_address FROM phpbb3_privmsgs
WHERE  message_time > UNIX_TIMESTAMP() - 3600
GROUP BY to_address, bcc_address
Wenn mehr als (in meinem Beispiel) 4 Nachrichten geschickt wurden ist private_messages 1 (true) ansonsten 0 (false). Dann brauchst Du dann nur noch die author_id herausfischen und dem das Senden an den entsprechenden Empfänger (to_address oder bcc_address) verbieten. Oder besser, Du verzögerst die Zustellung der Nachricht an den Empfänger.
Gruss, Miriam.
Ich schmeiß' alles hin und...
... lasse es liegen
Helmut
Mitglied
Beiträge: 2048
Registriert: 27.12.2002 20:35
Wohnort: Augsburg

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Helmut »

Hallo Miriam,

danke dir für deinen Lösungsansatz. Ich habe ihn gerade getestet, allerdings wird private_messages nicht TRUE auch wenn mehr als die 4 PN's an to_address gleich sind. Dann könnte auch ein Problem sein, wenn mehr als ein Empfänger in to_address eingetragen sind, darum hatte ich den Lösungsansatz mit der zweiten Tabelle PRIVMSGS_TO gewählt, da stehen sowohl Absender als auch Empfänger getrennt drinnen.

Code: Alles auswählen

        $sql = 'SELECT COUNT(msg_id) > 4 AS private_messages, author_id, to_address, bcc_address 
            FROM ' . PRIVMSGS_TABLE . '
                WHERE  message_time > UNIX_TIMESTAMP() - 3600
                GROUP BY to_address, bcc_address';
        $result = $db->sql_query($sql);
        
        $pm = (int) $db->sql_fetchfield('private_messages');
 
Ich habe meinen Entwurf auch mal an dein Konzept angepasst, aber auch das geht nicht so wie es soll. Hier ist dann private_messages immer TRUE, sobald mehr als 4 PN's geschrieben sind, auch wenn weniger an den gleichen Empfänger gehen.

Code: Alles auswählen

        $sql = 'SELECT COUNT(p.msg_id) > 4 AS private_messages, p.msg_id, p.author_id, p.message_time, t.msg_id, t.user_id
            FROM ' . PRIVMSGS_TABLE . ' p
            LEFT JOIN ' . PRIVMSGS_TO_TABLE . ' t ON (t.msg_id = p.msg_id)
                WHERE p.author_id =  ' . $user->data['user_id'] . '
                    AND t.user_id != t.author_id
                    AND message_time > UNIX_TIMESTAMP() - 3600
                    ORDER BY t.user_id, p.message_time';

        $result = $db->sql_query($sql);
        $pm = (int) $db->sql_fetchfield('private_messages');
        $db->sql_freeresult($result);


Gruß Helmut
Ich bin nicht ganz dicht.... na und.
Benutzeravatar
Miriam
Mitglied
Beiträge: 12310
Registriert: 13.10.2004 07:18
Kontaktdaten:

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Miriam »

Du könntest es so machen:
Finde in der /includes/functions.php
?>
davor füge ein:

Code: Alles auswählen

function chk_pm_count($sender = false, $recipient = false, $time = false)
{
    global $db;
    
    $pm_count_time_limit = (!$time) ?  time() - 3600 : time() - (int)$time;

    if(!$sender OR !$recipient) 
    {
        return;
    }
    $sql_array = array(
        'SELECT'    => 'COUNT(pmt.msg_id) AS messages',

        'FROM'      => array(
            PRIVMSGS_TO_TABLE => 'pmt'
        ),

        'LEFT_JOIN' => array(
            array(
                'FROM'  => array(PRIVMSGS_TABLE => 'pm'),
                'ON'    => 'pmt.msg_id = pm.msg_id'
            )
        ),

        'WHERE'            => 'pmt.user_id <> pmt.author_id
                AND pm.message_time > ' . $pm_count_time_limit . '
                AND pmt.author_id = ' . $sender . '
                AND pmt.user_id = ' . $recipient
    );

    $sql = $db->sql_build_query('SELECT', $sql_array);
    $result = $db->sql_query($sql);
    $data = $db->sql_fetchrow($result);
    $db->sql_freeresult($result);
    
    return $data['messages'];
}
Aufruf bspw. mit chk_pm_count($user->data['user_id'], $empfaenger, $zeitspanne)
so in etwa:

Code: Alles auswählen

if (chk_pm_count($user->data['user_id'], $empfaenger, $zeitspanne) > 5)
{
    trigger_error('Keine PN mehr!');
}
$zeitspanne kann auch leer sein, dann wird einen Stunde angenommen.

Kannst es ja noch etwas hübsch machen.
Gruss, Miriam.
Ich schmeiß' alles hin und...
... lasse es liegen
Helmut
Mitglied
Beiträge: 2048
Registriert: 27.12.2002 20:35
Wohnort: Augsburg

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Helmut »

Hallo Miriam,

im Prinzip geht dein neuer Lösungsansatz jetzt, allerdings gestaltet sich die Übergabe der Empfänger noch etwas schwierig.

Ich habe schon mal ein paar Einstellfunktionen für Parameter inklusive Berechtigungen ins ACP geschrieben, das funktioniert bestens. Ich muss nur noch was dazu bauen, dass an Mods und Admins trotz der Beschränkung eine PN geschrieben werden kann, ist aber nicht so eilig.

In der includes/ucp/ucp_pm_compose.php habe ich den ersten Teil vor $msg_id = submit_pm($action, $subject, $pm_data); gehängt. Zum testen habe ich mal eine feste user_id vorgegeben.

Code: Alles auswählen

            // Begin : PM Limit Per Hour
            if($config['pm_limit_hour_enable'] && ($action == 'post'))
            {                
                include($phpbb_root_path . 'includes/functions_pm_limit_hour.' . $phpEx);
                
                $zeitspanne = '';
                $to_user = 5;
                
                if (chk_pm_count($user->data['user_id'], $to_user, $zeitspanne) > $config['pm_limit_user_posts_hours'])
                {
                    trigger_error(sprintf($user->lang['PM_PREVENTED_MESSAGE_HOUR'], $config['pm_limit_user_posts_hours'], '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox') . '">', '</a>'));
                }
            }
            // End : PM Limit Per Hour
 
Die Funktion selber habe ich in eine eigene Datei gegeben, ist dann übersichtlicher und die functions.php wird nicht so groß.

functions_pm_limit_hour.php

Code: Alles auswählen

function chk_pm_count($sender = false, $recipient = false, $time = false)
{
    global $db, $user, $auth, $config, $phpbb_root_path, $phpEx;
    $user->setup('mods/pm_limit');
        
    if(!$auth->acl_get('u_pm_per_hour') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
    {
        $pm_count_time_limit = (!$time) ?  time() - 3600 : time() - (int)$time;

        if(!$sender OR !$recipient) 
        {
            return;
        }
        $sql_array = array(
            'SELECT'    => 'COUNT(pmt.msg_id) AS messages',

            'FROM'      => array(
                PRIVMSGS_TO_TABLE => 'pmt'
            ),

            'LEFT_JOIN' => array(
                array(
                'FROM'  => array(PRIVMSGS_TABLE => 'pm'),
                'ON'    => 'pmt.msg_id = pm.msg_id'
                )
            ),

            'WHERE' => 'pmt.user_id <> pmt.author_id
                AND pm.message_time > ' . $pm_count_time_limit . '
                AND pmt.author_id = ' . $sender . '
                AND pmt.user_id = ' . $recipient
        );

        $sql = $db->sql_build_query('SELECT', $sql_array);
        $result = $db->sql_query($sql);
        $data = $db->sql_fetchrow($result);
        $db->sql_freeresult($result);
     
    return $data['messages'];
    }
    return true;
} 
Ich habe jetzt das Problem, die user_id des/der Empfänger zu bekommen, diese sind bei der ucp_pm_compose.php in dem Array $address_list zu finden.



Gruß Helmut
Ich bin nicht ganz dicht.... na und.
Benutzeravatar
Miriam
Mitglied
Beiträge: 12310
Registriert: 13.10.2004 07:18
Kontaktdaten:

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Miriam »

Also die Empfänger IDs heraus zu bekommen ist einfach.
Dummerweise verhinderst Du u.U. das Senden der PMs an Empfänger, die noch nicht unter das Limit fallen.
Du könntest die Empfänger IDs ermitteln, die keine PM bekommen dürfen und diese vom Array $address_list abziehen. Dann verschickst Du die PMs und gibst danach eine Meldung aus, die den Absender unterrichtet, dass der und der Empfänger keine Nachricht bekommen haben, weil das Limit schon überschritten ist.
Gruss, Miriam.
Ich schmeiß' alles hin und...
... lasse es liegen
Helmut
Mitglied
Beiträge: 2048
Registriert: 27.12.2002 20:35
Wohnort: Augsburg

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Helmut »

Hallo Miriam,

ganz so Einfach ist es nicht, die Empfänger ID zu bekommen, denn $address_list ist ein verschachteltes Array, da wird für jeden Eintrag (Benutzer) ein Array angelegt, welches weiteres Arrays enthält, siehe hier:

ucp_pm_compose.php

Code: Alles auswählen

    // Build address list for display
    // array('u' => array($author_id => 'to'));
    if (sizeof($address_list))
    {
        // Get Usernames and Group Names
        $result = array();
        if (!empty($address_list['u']))
        {
            $sql = 'SELECT user_id as id, username as name, user_colour as colour
                FROM ' . USERS_TABLE . '
                WHERE ' . $db->sql_in_set('user_id', array_map('intval', array_keys($address_list['u']))) . '
                ORDER BY username_clean ASC';
            $result['u'] = $db->sql_query($sql);
        }

        if (!empty($address_list['g']))
        {
            $sql = 'SELECT g.group_id AS id, g.group_name AS name, g.group_colour AS colour, g.group_type
                FROM ' . GROUPS_TABLE . ' g';

            if (!$auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel'))
            {
                $sql .= ' LEFT JOIN ' . USER_GROUP_TABLE . ' ug
                    ON (
                        g.group_id = ug.group_id
                        AND ug.user_id = ' . $user->data['user_id'] . '
                        AND ug.user_pending = 0
                    )
                    WHERE (g.group_type <> ' . GROUP_HIDDEN . ' OR ug.user_id = ' . $user->data['user_id'] . ')';
            }

            $sql .= ($auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? ' WHERE ' : ' AND ';

            $sql .= 'g.group_receive_pm = 1
                AND ' . $db->sql_in_set('g.group_id', array_map('intval', array_keys($address_list['g']))) . '
                ORDER BY g.group_name ASC';

            $result['g'] = $db->sql_query($sql);
        }

        $u = $g = array();
        $_types = array('u', 'g');
        foreach ($_types as $type)
        {
            if (isset($result[$type]) && $result[$type])
            {
                while ($row = $db->sql_fetchrow($result[$type]))
                {
                    if ($type == 'g')
                    {
                        $row['name'] = ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['name']] : $row['name'];
                    }

                    ${$type}[$row['id']] = array('name' => $row['name'], 'colour' => $row['colour']);
                }
                $db->sql_freeresult($result[$type]);
            }
        }

        // Now Build the address list
        $plain_address_field = '';
        foreach ($address_list as $type => $adr_ary)
        {
            foreach ($adr_ary as $id => $field)
            {
                if (!isset(${$type}[$id]))
                {
                    unset($address_list[$type][$id]);
                    continue;
                }

                $field = ($field == 'to') ? 'to' : 'bcc';
                $type = ($type == 'u') ? 'u' : 'g';
                $id = (int) $id;

                $tpl_ary = array(
                    'IS_GROUP'    => ($type == 'g') ? true : false,
                    'IS_USER'    => ($type == 'u') ? true : false,
                    'UG_ID'        => $id,
                    'NAME'        => ${$type}[$id]['name'],
                    'COLOUR'    => (${$type}[$id]['colour']) ? '#' . ${$type}[$id]['colour'] : '',
                    'TYPE'        => $type,
                );

                if ($type == 'u')
                {
                    $tpl_ary = array_merge($tpl_ary, array(
                        'U_VIEW'        => get_username_string('profile', $id, ${$type}[$id]['name'], ${$type}[$id]['colour']),
                        'NAME_FULL'        => get_username_string('full', $id, ${$type}[$id]['name'], ${$type}[$id]['colour']),
                    ));
                }
                else
                {
                    $tpl_ary = array_merge($tpl_ary, array(
                        'U_VIEW'        => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&g=' . $id),
                    ));
                }

                $template->assign_block_vars($field . '_recipient', $tpl_ary);
            }
        }
    }
 
Den Code habe ich in meine functions_pm_limit_hour.php genommen, das geht auch schon teilweise.

Stimmt die Überlegung hatte ich auch schon, was passiert mit den Empfängern, die das Limit noch nicht überschritten haben. Deine Möglichkeit ist mir auch schon in den Sinn gekommen, aber in Anbetracht des verschachtelten Arrays scheint es mir schon etwas Aufwendig zu sein. Da stellt sich auch die Frage, die betreffenden Empfänger raus nehmen und trotzdem dann versenden, oder nicht versenden und die betreffenden Empfänger nur anzeigen und dem User überlassen ob er diese dann raus nimmt.

Ich habe da noch eine andere Idee, vielleicht könnte es gehen, gleich an der Stelle zu prüfen wo der Benutzer ins Array geschrieben werden soll, ist vermutlich die Funktion handle_message_list_actions. Ich muss mir auch noch die $to_user_id anschauen, könnte auch interessant sein. Vermutlich muss ich irgendwo hier angreifen:

Code: Alles auswählen

    {
        $message_attachment = 0;
        $message_text = $message_subject = '';

        if ($to_user_id && $to_user_id != ANONYMOUS && $action == 'post')
        {
            $address_list['u'][$to_user_id] = 'to';
        }
        else if ($to_group_id && $action == 'post')
        {
            $address_list['g'][$to_group_id] = 'to';
        }
        $check_value = 0;
    }
 
aber wenn auf eine Mail geantwortet wird, dann steht die Empfänger ID vermutlich schon in $address_list drinnen, habe ich aber noch nicht geprüft. Wenn dem so ist, dann kommt vermutlich doch wieder die erste Lösung in Betracht, also nicht ganz einfach.

Den in meinem letzten Post genannten Code (// Begin : PM Limit Per Hour) habe ich zur Zeit mal bei if ($submit) im Fehlerteil drinnen, damit die Meldung noch vor dem versenden kommt.


Insgesamt erweist sich die vermeintlich einfache Lösung doch als sehr anspruchsvoll in der Umsetzung.

Gruß Helmut
Ich bin nicht ganz dicht.... na und.
Benutzeravatar
Miriam
Mitglied
Beiträge: 12310
Registriert: 13.10.2004 07:18
Kontaktdaten:

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Miriam »

Helmut hat geschrieben:...ganz so Einfach ist es nicht, die Empfänger ID zu bekommen...
:o Doch, ist es:
...denn $address_list ist ein verschachteltes Array, da wird für jeden Eintrag (Benutzer) ein Array angelegt, welches weiteres Arrays enthält...
Bist Du sicher, wer macht denn sowas?
Ich betrachte in Moment nur die PMs an Benutzer (egal, ob "to" oder "bcc"); also das Array $address_list['u'].

Idee eines weiteren Ansatzes:
  • Code: Alles auswählen

                if($config['pm_limit_hour_enable'] && ($action == 'post') && sizeof($address_list['u'])
                {
                    include($phpbb_root_path . 'includes/functions_pm_limit_hour.' . $phpEx);
                    $zeitspanne = '';
                    foreach ($address_list['u'] as $to_user => $value)
                    {
                        if (chk_pm_count($user->data['user_id'], $to_user, $zeitspanne) > $config['pm_limit_user_posts_hours'])
                        {
                            $error_message = sprintf($user->lang['PM_PREVENTED_MESSAGE_HOUR'], $config['pm_limit_user_posts_hours']);
                        }
                    }
                } 
Und $error_message verknüpfst Du weiter unten mit $message. Dann werden die Nachrichten ausgegeben.
Gruss, Miriam.
Ich schmeiß' alles hin und...
... lasse es liegen
Helmut
Mitglied
Beiträge: 2048
Registriert: 27.12.2002 20:35
Wohnort: Augsburg

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Helmut »

Hallo Miriam,

da hab ich wohl etwas zu komplex gedacht, so geht es natürlich auch. Ich habe es mal so zum testen eingebaut:

Code: Alles auswählen

        // Subject defined
        if ($submit)
        {
            if (utf8_clean_string($subject) === '')
            {
                $error[] = $user->lang['EMPTY_MESSAGE_SUBJECT'];
            }

            if (!sizeof($address_list))
            {
                $error[] = $user->lang['NO_RECIPIENT'];
            }

            // Begin : PM Limit Per Hour
            if($config['pm_limit_hour_enable'] && ($action == 'post') && sizeof($address_list['u']))
            {
                include($phpbb_root_path . 'includes/functions_pm_limit_hour.' . $phpEx);
                
                $zeitspanne = ($config['pm_limit_hours_user_compose']);
                foreach ($address_list['u'] as $to_user => $value)
                {
                    if (chk_pm_count($user->data['user_id'], $to_user, $zeitspanne, $address_list) > $config['pm_limit_user_posts_hours'])
                    {
                        $error[] = (sprintf($user->lang['PM_PREVENTED_MESSAGE_HOUR'], $config['pm_limit_user_posts_hours']));
                    }
                }
            }             
            // End : PM Limit Per Hour
                        
        }

        // Store message, sync counters
        if (!sizeof($error) && $submit)
Das funktioniert soweit ganz gut. Ich habe mich entschlossen, in der Fehlermeldung dann die Empfänger anzuzeigen, welche das Limit erreicht haben, dann kann der User selber entscheiden ob er die jeweiligen Empfänger raus nimmt oder die PN nicht verschickt. Die Gruppen auch noch zu berücksichtigen, denke ich führt zu weit, ich lasse PN's eh nicht von normalen Usern an Gruppen versenden.

Das Array $address_list ist schon verschachtelt, kannst dir damit schön anzeigen lassen:

Code: Alles auswählen

echo "<pre>";
print_r($address_list);
echo "</pre>";
 
Wenn die Anzeige vor // Build hidden address list in die ucp_pm_compose.php rein hängst, dann siehst es praktisch vollständig gefüllt.

Gruß Helmut
Ich bin nicht ganz dicht.... na und.
Benutzeravatar
Miriam
Mitglied
Beiträge: 12310
Registriert: 13.10.2004 07:18
Kontaktdaten:

Re: Abfrage wieviele PN's an den gleichen Empfänger

Beitrag von Miriam »

Was hältst Du von der Idee, die PMs zurückzuhalten, bis die Anzahl der PMs im Zeitlimit unter den kritischen Wert gefallen ist und dann alle noch nicht gelieferten Nachrichten mit einem Rutsch freizugegen?
Gruss, Miriam.
Ich schmeiß' alles hin und...
... lasse es liegen
Antworten

Zurück zu „[3.0.x] Mod Bastelstube“