This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Best way to get Nickserv Identification Status of users

General support and discussion of Eggdrop bots.
Post Reply
S
Sarteck
Voice
Posts: 11
Joined: Fri Mar 14, 2008 9:14 pm

Best way to get Nickserv Identification Status of users

Post by Sarteck »

Until recently, I've been running a PHP-IRC bot, because PHP is the language I'm most comfortable with. Unfortunately, it's just consuming too much CPU (which I was warned beforehand that it would, heh), so I'm trying to switch a lot of my functions over from PHP to Tcl, which I know next to nothing about. I've been getting the hang of the syntax easily enough, but there's just so much I don't know how to do. XD



Anyways, I used to have this timer set on the PHP Bot to check the user list every 3 minutes, and for each user it found that was not already in its namelist array (or was in it but without a status of 3), it added that user as the array key and their status code (0, 1, 2, or 3, NickServ's various Identification levels).

For example:
(PHP example)

Code: Select all

$userlist = array(
  'Tom' => 3, // Identified
  'Dick' => 0, // Nick Not Registered
  'Harry => 1 // Nick Registered, but not Identified as owner
)



I have been reading, and my understanding is that Tcl arrays are ONLY associative arrays--they are not indexes with numbers, like a lot of the languages I'm familiar with. That's cool, though, because what I've been using is an associative array.

The problem arises when I realize I don't know how I can make eggdrop listen for NOTICE responses from NickServ when it sends queries about the status. Or maybe I'm going about this entirely the wrong way, I'm not sure. XP





BASICALLY, I want to make sure that the users executing certain commands on the bot are IDENTIFIED with NickServ. I'd like to take that info and send it via POST to a PHP script, which will parse it and send it in turn to a MySQL server. (Later on down the line, I'd like to see if I can use Tcl to directly interface with MySQL, but there's a lot of PHP I'd have to re-write into Tcl in order to get my script running right, so for now I'll stick with Tcl -> POST for PHP -> MySQL.)

I'm already aware of how to send the POST data, have made a script, tested it, and everything works out A-OK.





If I could be allowed to double-post, I'll post a snippet of the PHP code in the next post, to give you a general idea of what I was doing. I know that my way was probably not the most elegant of solutions, but it worked. XP
S
Sarteck
Voice
Posts: 11
Joined: Fri Mar 14, 2008 9:14 pm

The PHP snippet

Post by Sarteck »

Code: Select all


  /// RESPONDS to Notices, adds/changes nicks to master table.
  public function check_incoming($line, $args)
  {
    if (strtolower($line['fromNick']) != 'nickserv') {return;}
    if ($args['cmd'] != 'status')        {return;}
    if ($args['arg2'] <= -1)             {return;}
    if ($args['arg2'] >= 4)              {return;}
    if (array_key_exists('arg3', $args)) {return;}
    $this->_set_namelist($args['arg1'], $args['arg2']);
  }

  /// !get_names --- Gets list of names on the current channel and sends it to the parser
  public function get_names($line=false, $args=false)
  {
    $channels = array('#void','#ClubBleach','#4thDivision','#Junibantai','#cbstaff','#die','#Central46');
    $new_channel_index = ($this->last_channel_index >= (sizeof($channels)-1)) ? 0 : $this->last_channel_index + 1;
    $channel = $channels[$new_channel_index];
    $this->ircClass->sendRaw('names ' . $channel);
    $this->getting_channel = $channel;
    $this->last_channel_index = $new_channel_index;
    return true;
  }

  /// RESPONDS to 353 --- For each Name provided by get_names, sends a /msg NickServ STATUS $name
  public function parse_names($line, $args)
  {
    $names = explode(' ', $line['text']);
    $channel = '';
    if ($this->getting_channel)
    {
      $channel = $this->getting_channel;
      $this->getting_channel = false;
      $this->channel_list[$channel] = $names;
    }
    foreach ($names AS $name)
    {
      $name = $this->_get_real_name($name);
      $this->ircClass->privMsg('NickServ', 'STATUS ' . $name);
    }
    return true;
  }
S
Sarteck
Voice
Posts: 11
Joined: Fri Mar 14, 2008 9:14 pm

Post by Sarteck »

:facepalms:

Sorry, Mods; please move this to the Scripting Help forum at your leisure. :(
http://forum.egghelp.org/viewforum.php?f=2

My bad.
S
Sarteck
Voice
Posts: 11
Joined: Fri Mar 14, 2008 9:14 pm

Post by Sarteck »

Quadruple posting ftw! :D



No, I'm posting to tell ya I think I've got it figured out now, after a bit of digging. Although I cannot find a way to make eggdrop listen for the 353 line returned from the NAMES command, I don't need it.


Code: Select all

chanlist <channel>  [flags[&chanflags]]

    Description: flags are any global flags; the '&' denotes to look for channel specific flags. Examples:

        n
        	

        Global Owner

        &n
        	

        Channel Owner

        o&m
        	

        Global Op, Channel Master

    Returns: list of nicknames currently on the bot's channel that have all of the flags specified;. If no flags are given, all of the nicknames are returned. Please note that if you're executing chanlist after a part or sign bind, the gone user will still be listed, so you can check for wasop, isop, etc.
Seems that chanlist will work just great for me, and I don't have to parse user masks like I was doing with my PHP bot. X3



Here's the code I'm working with--mind you it's not tested yet.


Code: Select all

bind notc - "*STATUS*" doSetStatus
bind time - * doGetNames



### Get Names on channel, send Status request to NickServ for each name on nicklist without STATUS of 3
### -- CALLED EVERY MINUTE by bind time - *
proc doGetNames {nick host handle text dest} {
  global nicklist
  foreach userraw [chanlist "#void"] {
    if {nicklist($userraw) != 3} {
      putserv "PRIVMSG NickServ :STATUS $userraw"
    }
  }
}

### Answers NickServ's status response notice, setting nicklist appropriately.
proc doSetStatus {nick host handle text dest} {
  global nicklist
  regexp {STATUS ([^ ]*) ([0-3])} $text NS_name NS_status
  set nicklist($NS_name) $NS_status
}
I've probably got a few bugs, but I'm sure I'm on the right track. I'll make this a quintuple post to let you guys know how it turns out when it's done. :P



Eventually, this list of identified nicks will be able to display a "Who's Online" on the main page of our forum... or at least that's my intent. I'm aware there's already such a script out there, but mine would have to be modified to get the forum owner of the IRC nickname, something it would have to interface with the database for.
w
willyw
Revered One
Posts: 1202
Joined: Thu Jan 15, 2009 12:55 am

Post by willyw »

I'm not sure that I'm with you all the way, but I do have a couple comments. I hope they are useful to you. :)
Sarteck wrote:Quadruple posting ftw! :D

No, I'm posting to tell ya I think I've got it figured out now, after a bit of digging. Although I cannot find a way to make eggdrop listen for the 353 line returned from the NAMES command, I don't need it.
Sounds like you want to watch for a raw numeric from the server.
Is that it?

Code: Select all

bind raw <flags> <keyword> <proc>
will do it. (if you change your mind and want to... or ever need this idea for something else)
Keyword in this case would be 353.

Look in your doc/ directory, for a file named tcl-commands.doc
How to use this bind is described in there, along with all the rest of the eggdrop specific tcl commands.


To make it easier to see what is going on, when experimenting like this - I issue this command in the partyline: .console +rv
Then, I can see raw incoming and outgoing traffic.

More info on the console flags can be had with : .help console

Code: Select all

chanlist <channel>  [flags[&chanflags]]

    Description: flags are any global flags; the '&' denotes to look for channel specific flags. Examples:

        n
        	

        Global Owner

        &n
        	

        Channel Owner

        o&m
        	

        Global Op, Channel Master

    Returns: list of nicknames currently on the bot's channel that have all of the flags specified;. If no flags are given, all of the nicknames are returned. Please note that if you're executing chanlist after a part or sign bind, the gone user will still be listed, so you can check for wasop, isop, etc.
Seems that chanlist will work just great for me, and I don't have to parse user masks like I was doing with my PHP bot. X3
...
This applies only to users that have an account in your bot.

If I understand you correctly, you wanted a way to determine if users in a channel were id'd with Nickserv.

What if there are nicks in the channel, that are id'd with Nickserv, but do not have accounts in your bot?
Are they important?
w
willyw
Revered One
Posts: 1202
Joined: Thu Jan 15, 2009 12:55 am

Re: Best way to get Nickserv Identification Status of users

Post by willyw »

Sarteck wrote: ...

BASICALLY, I want to make sure that the users executing certain commands on the bot are IDENTIFIED with NickServ.
...
For what it is worth:


I've done this in the past.... ... made a script that would check to see if users were id'd with Nickserv or not.

However, I did it by checking for raw numerics, by having the bot send a /whois command on each nick in question, to the server.
Using raw binds, the return from the /whois command can be examined to determine if the user is id'd.

It worked. The problem can be that the script might not necessarily work on another server. The raw numerics returned can be a bit different, necessitating adjusting the script for each server.

I completely overlooked the /msg nickserv status
command. ... just never thought of it.
That's an interesting way of going about it, and I haven't tried to figure out which way is "best"... or if one even is "best".


Good luck with your project. :)
S
Sarteck
Voice
Posts: 11
Joined: Fri Mar 14, 2008 9:14 pm

Post by Sarteck »

Hey, Willy, thanks for that--I was assuming that chanlist would return all the names on a specified channel the bot is on--I didn't realize that it only takes into account those with an account on the bot. Well, if that's the case, I'll have to go about it with (as you said) using the raw bind.

Ah... On closer inspection... it appears you were mistaken. >.> Or mebbe you didn't know what I was asking for, I'm not sure. X3

Code: Select all

set nicklist("ClubBleach") 3

bind pub - !randomhan setRandomHan
bind notc - "*STATUS*" doSetStatus
bind time - "* * * * *" doGetNames

###############################################################################
###                Procedures called by public bind events                  ###
###############################################################################


### Get valid users for randomhan, process CB Users, give han, report to channel
proc setRandomHan {nick host handle chan text} {
  global nicklist
  set numUsers $text
  set identifiedUsers ""
  foreach userraw [chanlist "#ClubBleach"] {
    if {[info exists nicklist($userraw)]} {
      if {$nicklist($userraw) == 3} {
        lappend identifiedUsers $userraw
      }
    }
  }
  set CBUserData [setCBUsers $identifiedUsers $nick $numUsers]
  foreach userline [split $CBUserData "@"] {
    putserv "PRIVMSG #ClubBleach :$userline"
  }
}


### Get Names on channel, send Status request to NickServ for each name on nicklist without STATUS of 3
### -- CALLED EVERY MINUTE by bind time - "* * * * *"
proc doGetNames {min hour day month year} {
  global nicklist
  foreach userraw [chanlist "#ClubBleach"] {
    if {![info exists nicklist($userraw)]} {
      set nicklist($userraw) 0
    }
    if {$nicklist($userraw) != 3} {
      putserv "PRIVMSG NickServ :STATUS $userraw"
    }
  }
}



### Answers NickServ's status response notice, setting nicklist appropriately.
proc doSetStatus {nick host handle text dest} {
  global nicklist
  regexp {STATUS ([^ ]*) ([0-3])} $text hmm NS_name NS_status
  set nicklist($NS_name) $NS_status
  putserv "PRIVMSG Sarteck :$NS_name -- $NS_status"
}





### Get valid CB Users frim Identified User list.
### Process the random han in PHP, and then return the result to the bot to display in the chan.
proc setCBUsers {iduserlist nick numUsers} {
  package require http
  set url http://www.XXXXXXXXXXXXXXXX.org/mybot.php
  set querystring [http::formatQuery "do" "new_randomhan" "users" $iduserlist "caller" $nick "numUsers" $numUsers]
  set token [http::geturl $url -query $querystring]
  set data [http::data $token]
  http::cleanup $token
  return $data
}
This is my code, and it works perfcetly, except for one bit....




Code: Select all

proc setRandomHan {nick host handle chan text} {
  global nicklist
  set numUsers $text
  set identifiedUsers ""
  foreach userraw [chanlist "#ClubBleach"] {
    if {[info exists nicklist($userraw)]} {
      if {$nicklist($userraw) == 3} {
        lappend identifiedUsers $userraw
      }
    }
  }
  set CBUserData [setCBUsers $identifiedUsers $nick $numUsers]
  foreach userline [split $CBUserData "@"] {
    putserv "PRIVMSG #ClubBleach :$userline"
  }
}
The CBUsers procedure returns a string from the page it loads. This string is actually a list separated by "@" symbols. It works fine with about 10 or less users, but if I put in, say, 20 "lines" to CBUserData (each "line" separated by the "@" symbol), it usually stops after about 12-14 lines and doesn't PRIVMSG the channel with the rest. I'm not sure what I'm doing wrong there.
w
willyw
Revered One
Posts: 1202
Joined: Thu Jan 15, 2009 12:55 am

Post by willyw »

Sarteck wrote:Hey, Willy, thanks for that--I was assuming that chanlist would return all the names on a specified channel the bot is on--
It can.
I didn't realize that it only takes into account those with an account on the bot.
It can, if an optional flag is used with the command.
...
Or mebbe you didn't know what I was asking for, I'm not sure. X3
It was my error, as I focused on that which you'd quoted, which included the optional flag command.

Obviously, you do understand the use of the command. :)

This is my code, and it works perfectly, except for one bit....

....


set CBUserData [setCBUsers $identifiedUsers $nick $numUsers]
foreach userline [split $CBUserData "@"] {
putserv "PRIVMSG #ClubBleach :$userline"
}
}[/code]

The CBUsers procedure returns a string from the page it loads. This string is actually a list separated by "@" symbols. It works fine with about 10 or less users, but if I put in, say, 20 "lines" to CBUserData (each "line" separated by the "@" symbol), it usually stops after about 12-14 lines and doesn't PRIVMSG the channel with the rest. I'm not sure what I'm doing wrong there.

Without having anything to actually test and experiment with, it is much harder for me to try to figure out. (Perhaps you need a real TCL guru, eh? :) )
But I'll venture this:
I noticed this line:

Code: Select all

foreach userline [split $CBUserData "@"] {
Not sure what effect using foreach might have.
When I do something like that, I do:

Code: Select all

set userline [split $CBUserData "@"] {
I would have tested it, before posting..... this might be a waste of your time.... but give it a test. See what difference it makes.



I like your proc doSetStatus for determing if a nick is id'd with Nickserv.
S
Sarteck
Voice
Posts: 11
Joined: Fri Mar 14, 2008 9:14 pm

Post by Sarteck »

See, setRandomHan gives a random amount of "Hansatsu" (a "currency" on my forum) to a specified amount of users.

CBUserData gets the result from the CBUsers function. THat function calls a PHP page on my server through POST. The PHP Script sends back a string response (I make it "die($data)"), which goes to the CBUserData variable.

This string is actually a PHP Array that's been "imploded()" with "@" as the implode character.

If I had the PHP array

Code: Select all

$data = array('Tom got 50 Han', 'Dick got 20 Han', 'Harry got 10 Han');
die(implode('@',$data));
Then CBUserData would now contain
'Tom got 50 Han@Dick got 20 Han@Harry got 10 Han'



So, I split that on the "@" character in Tcl to use a "foreach" that puts each message on one line. Get it?



The problem is, it seems to only send the first 12-14 lines. If I give Random Han to 20 or 30 people, it PRIVMSGs to the channel only 12-14 times. I do know that the PHP page sends the string in its entirety, but I'm wondering if maybe Tcl could be trimming it somehow? Would I have to declare a variable to be able to hold large amounts of text? Or is it something else entirely?
S
Sarteck
Voice
Posts: 11
Joined: Fri Mar 14, 2008 9:14 pm

Post by Sarteck »

Just a follow up, here.

THe "bug" which made it seem as if my Bot was not sending every line of the split string; well, it turned out to be that it just would not repeat the same message twice or more in a row.

So if one of my users got awarded 2 Han, then the same user got awarded the same amount, since both the strings are the same, it wouldn't send the second line.

I don't know if that's a Tcl thing, or if it's an eggdrop thing, or simply if the server I'm on is what's not allowing it. However, I "fixed" it by putting an incrementing variable in the message line--thus, none of the lines are ever the same for the same run. X3



I based another script off of that, btw, to list which users are online on my forum home page. :3 It's pretty sweet.



No all I have to do is put in some kind of check to make sure that the NOTICES it receives back from NickServ are indeed from NickServ. (The way I currently have it written, it just checks for any notice beginning with "STATUS").

I also put in a timer to clear entries from the main nicklist variable every 15 minutes, so that it's not full of a bunch of "dead" users.
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

That's a feature in fairly recent eggdrops. See the double-* settings in your config-file (current would be double-mode, double-server, double-help).
NML_375
Post Reply