Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ optional arguments:
-m MAX_AGE, --max_age MAX_AGE
only show issues less than MAX_AGE days old
--cooldown_min do not repeat lookups within the given number of minutes
--nickserv_cloak NICKSERV_CLOAK
Only try to identify if nickserv has this host (as we aren't using sasl)
--nickserv_password NICKSERV_PASSWORD
Nickserv password to use with the given nickname as account
```

### Example:
Expand Down
82 changes: 81 additions & 1 deletion neo-bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,18 @@

class GitHubBot(irc.bot.SingleServerIRCBot):
def __init__(
self, api, channel, nickname, server, port, user, repo, max_age, cooldown_min
self,
api,
channel,
nickname,
server,
port,
user,
repo,
max_age,
cooldown_min,
nickserv_cloak,
nickserv_password,
):
super().__init__(((server, port),), nickname, nickname)
self.api = api
Expand All @@ -37,6 +48,13 @@ def __init__(
self.user = user
self.repo = repo
self.nickname = nickname

# nickserv stuff
self.account = nickname # save our original, non extended nick for auth
self.nickserv_password = nickserv_password
self.nickserv_cloak = nickserv_cloak
self._is_doing_auth = False

self.max_age = timedelta(days=max_age)
self.policies = [
self.reject_if_too_old(),
Expand All @@ -48,6 +66,7 @@ def on_nicknameinuse(self, c, e):
self.nickname = c.get_nickname()

def on_welcome(self, c, e):
self._start_auth(c)
c.join(self.channel)

def on_privmsg(self, c, e):
Expand All @@ -64,6 +83,54 @@ def _apply_report_policies(self, msg, entity):
return resp
return None

_auth_events = ("whoreply", "nosuchserver")

def _start_auth(self, c):
if self.nickserv_password is None:
return

self._is_doing_auth = True
# we want to know that we are talking to services, so we first ask whom we are talking to.
# Note this is inherently racy but the best we can do without sasl
for ev in self._auth_events:
c.add_global_handler(ev, self._do_auth)
c.who("nickserv")

def _validate_should_auth(self, e):
"""Raises a ValueError if we shouldn't try to auth"""
if not self._is_doing_auth:
raise ValueError("ERROR: auth callback called but not initiated by us")

if e.type == "nosuchserver":
raise ValueError("ERROR: nickserv is unknown")

if e.type != "whoreply":
raise ValueError(f"ERROR: got invalid event {e}")

def _check_nickserv(self, e):
if len(e.arguments) < 3:
raise ValueError(f"ERROR: got invalid event {e}")
username = e.arguments[1]
host = e.arguments[2]
if username.lower() != "nickserv" or host != self.nickserv_cloak:
raise ValueError(
f"ERROR: username <{username}> not nickserv "
+ f"or host {host} doesn't match {self.nickserv_cloak}"
)

def _do_auth(self, c, e):
"""Fired when we should try to auth"""
try:
self._validate_should_auth(e)
self._check_nickserv(e)
c.privmsg("nickserv", f"IDENTIFY {self.account} {self.nickserv_password}")
except ValueError as err:
print(err)
finally:
for ev in self._auth_events:
c.remove_global_handler(ev, self._do_auth)
self._is_doing_auth = False

def _process_message(self, c, answer_to, e):
msgs = e.arguments
for msg in msgs:
Expand Down Expand Up @@ -184,6 +251,17 @@ def parse_args():
type=int,
default=5,
)
parser.add_argument(
"--nickserv_cloak",
help="Only try to identify if nickserv has this host (as we aren't using sasl)",
type=str,
default="services.libera.chat",
)
parser.add_argument(
"--nickserv_password",
help="Nickserv password to use with the given nickname as account",
type=str,
)
return parser.parse_args()


Expand All @@ -202,6 +280,8 @@ def main():
args.repo,
args.max_age,
args.cooldown_min,
args.nickserv_cloak,
args.nickserv_password,
)
bot.start()

Expand Down