Repository: sopel-irc/sopel

Issue:
Make Willie join channels after successfully identifying to NickServ
At the moment, Willie identifies and joins default channels at the same time when connecting:

```
>>1375755742.03 PRIVMSG NickServ :IDENTIFY password
>>1375755742.03 MODE  Ampharos +p
>>1375755742.03 JOIN #chan1
>>1375755742.03 JOIN #chan2
```

This is troublesome if one of those channels has `+R` (registered-only), as the join will fail, since chances are, Willie has not identified yet:

```
<<1375755742.06 :irc.rizon.net 376 Ampharos :End of /MOTD command.
<<1375755742.06 :Ampharos!~amphy@Rizon-5AE7B3E.pm MODE Ampharos :+Six
<<1375755742.18 :Ampharos!~amphy@Rizon-5AE7B3E.pm MODE Ampharos :+p
<<1375755742.18 :irc.rizon.net 477 Ampharos #chan1 :You need to identify to a registered nick to join that channel. Check /msg nickserv help register
<<1375755742.18 :Ampharos!~amphy@Rizon-5AE7B3E.pm JOIN :#chan2
...
<<1375755742.37 :Ampharos!~amphy@Rizon-5AE7B3E.pm MODE Ampharos :+r
<<1375755742.37 :NickServ!service@rizon.net NOTICE Ampharos :Password accepted - you are now recognized.
```

Of course, it can be solved by manually tell the bot to join the `+R` channel after connection, but not all that convenient.

So I suggest that Willie waits a second or two after identifying (if it has to) before joining its channels. Or alternatively, wait for NickServ to tell the bot it has identified.



Discussion:
For some networks, like Freenode, this will be solved by SASL authentication which was just added and will be getting refined per #312. It doesn't look like Rizon supports SASL, though, so that won't help you.

Detecting NickServ auth in any reliable way is impossible. Every set of services sends that ack in a different way. We could add a pause, but I'm not sure about the idea of making startup even slower for an uncommon case that will hopefully go away as IRCv3 adoption gets more common.

Freenode also supports encoding nickserv password in the server password
field, which is faster than using nickserv

Elad Alfassa
On Aug 16, 2013 1:27 AM, "Edward powell" notifications@github.com wrote:

> For some networks, like Freenode, this will be solved by SASL
> authentication which was just added and will be getting refined per #312https://github.com/embolalia/willie/issues/312.
> It doesn't look like Rizon supports SASL, though, so that won't help you.
> 
> Detecting NickServ auth in any reliable way is impossible. Every set of
> services sends that ack in a different way. We could add a pause, but I'm
> not sure about the idea of making startup even slower for an uncommon case
> that will hopefully go away as IRCv3 adoption gets more common.
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/embolalia/willie/issues/310#issuecomment-22735471
> .

Another possibility would be to detect event 477 ("you need a registered nick to join") and keep retrying the `JOIN` to that channel, which will give enough time to identify.

Only thing is that both trigger.group and trigger.bytes only return the message, and not the channel that was attempted. 

Raw message example:

```
:irc.rizon.us 477 Bui #chan :You need to identify to a registered nick to join that channel. Check /msg nickserv help register
```

For the non-startup case (e.g., .join), prior to joining a channel, could check to see if the channel is set mode +R and if the bot is set mode +r. For the startup case you could check all channels on the list of channels to join on startup to see if any are set +R, and if they are to wait for user mode +r before joining.

That much checking of modes back and forth is just begging for problems, and would be a pain to debug.

@bui has the best idea. I'm sure there's some way to pull the channel out of there somehow.

I've tried to use `bot.raw`, but it's possible for the bot to receive their join line for another channel in the time the retry function is called.

```
<<1377179107.4  :irc.rizon.us 477 Ampharos #chan :You need to identify to a registered nick to join that channel. Check /msg nickserv help register
<<1377179107.4  :Ampharos!~amphy@Rizon-5AE7B3E.pm JOIN :#chan2
>>1377179107.4  JOIN #chan2
```

So because of that, Willie thinks the failed channel was `#chan2` and joins that instead, which does nothing since it has already been joined.

@embolalia Do you think it would be possible for `willie.raw` to become a list of the most recent raw lines? Could also come in handy in the future.

On second though, I think it'd be better if `trigger.raw` was created. Much more accurate.

I'll do that, fix up this issue and submit a pull once tested.

Yeah, bot.raw is completely wrong. We (I assume) added that before we had any idea what we we were doing. trigger.raw is how it should be.


Resolving patch:
diff --git a/willie/bot.py b/willie/bot.py
--- a/willie/bot.py
+++ b/willie/bot.py
@@ -513,7 +513,6 @@ def __getattr__(self, attr):
     class Trigger(unicode):
         def __new__(cls, text, origin, bytes, match, event, args, self):
             s = unicode.__new__(cls, text)
-            s.raw = origin.raw
             s.sender = origin.sender
             """
             The channel (or nick, in a private message) from which the
diff --git a/willie/coretasks.py b/willie/coretasks.py
--- a/willie/coretasks.py
+++ b/willie/coretasks.py
@@ -51,7 +51,7 @@ def startup(bot, trigger):
 
     bot.memory['retry_join'] = dict()
     for channel in bot.config.core.get_list('channels'):
-        bot.write(('JOIN', channel))
+        bot.join(channel)
 
 
 @willie.module.event('477')
@@ -62,7 +62,7 @@ def retry_join(bot, trigger):
     Give NickServ enough time to identify, and retry rejoining an
     identified-only (+R) channel. Maximum of ten rejoin attempts.
     """
-    channel = re.search('477 %s (.*?) :' % bot.nick, trigger.raw).group(1)
+    channel = trigger.args[1]
     if channel in bot.memory['retry_join'].keys():
         bot.memory['retry_join'][channel] += 1
         if bot.memory['retry_join'][channel] > 10:
@@ -70,11 +70,11 @@ def retry_join(bot, trigger):
             return
     else:
         bot.memory['retry_join'][channel] = 0
-        bot.write(('JOIN', channel))
+        bot.join(channel)
         return
 
     time.sleep(6)
-    bot.write(('JOIN', channel))
+    bot.join(channel)
 
 #Functions to maintain a list of chanops in all of willie's channels.
 
diff --git a/willie/irc.py b/willie/irc.py
--- a/willie/irc.py
+++ b/willie/irc.py
@@ -39,12 +39,9 @@
 class Origin(object):
     source = re.compile(r'([^!]*)!?([^@]*)@?(.*)')
 
-    def __init__(self, bot, raw, source, args):
+    def __init__(self, bot, source, args):
         self.hostmask = source
 
-        # Full raw line from server
-        self.raw = raw
-
         #Split out the nick, user, and host from hostmask per the regex above.
         match = Origin.source.match(source or '')
         self.nick, self.user, self.host = match.groups()
@@ -396,8 +393,7 @@ def found_terminator(self):
         if line.endswith('\r'):
             line = line[:-1]
         self.buffer = u''
-        raw = line
-        self.raw = raw
+        self.raw = line
         if line.startswith(':'):
             source, line = line[1:].split(' ', 1)
         else:
@@ -422,7 +418,7 @@ def found_terminator(self):
             stderr('Nickname already in use!')
             self.handle_close()
 
-        origin = Origin(self, raw, source, args)
+        origin = Origin(self, source, args)
         self.dispatch(origin, text, args)
 
     def dispatch(self, origin, text, args):


Test patch: