#!/bin/bash # -*- coding: utf-8 -*- ########################################################################### # # # envbot - an IRC bot in bash # # Copyright (C) 2007-2008 Arvid Norlander # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # # # ########################################################################### #--------------------------------------------------------------------- ## Server connection. #--------------------------------------------------------------------- # Server info variables #--------------------------------------------------------------------- ## Name of server (example: server1.example.net) ## @Type API #--------------------------------------------------------------------- server_name="" #--------------------------------------------------------------------- ## The 004 received from the server. ## @Type API #--------------------------------------------------------------------- server_004="" #--------------------------------------------------------------------- ## The 005 received from the server. Use parse_005 to get data out of this. ## @Type API ## @Note See http://www.irc.org/tech_docs/005.html for an incomplete list of 005 values. #--------------------------------------------------------------------- server_005="" # NAMES output with UHNAMES and NAMESX # :photon.kuonet-ng.org 353 envbot = #bots :@%+AnMaster!AnMaster@staff.kuonet-ng.org @ChanServ!ChanServ@services.kuonet-ng.org bashbot!rfc3092@1F1794B2:769091B3 # NAMES output with NAMESX only: # :hurricane.KuoNET.org 353 envbot = #test :bashbot ~@Brain ~@EmErgE &@AnMaster/kng #--------------------------------------------------------------------- ## 1 if UHNAMES enabled, otherwise 0 ## @Type API #--------------------------------------------------------------------- server_UHNAMES=0 #--------------------------------------------------------------------- ## 1 if NAMESX enabled, otherwise 0 ## @Type API #--------------------------------------------------------------------- server_NAMESX=0 # These are passed in a slightly odd way in 005 so we do them here. #--------------------------------------------------------------------- ## The mode char (if any) for ban excepts (normally +e) ## @Type API #--------------------------------------------------------------------- server_EXCEPTS="" #--------------------------------------------------------------------- ## The mode char (if any) for invite excepts (normally +I) ## @Type API #--------------------------------------------------------------------- server_INVEX="" # In case we don't get a 005, make some sane defaults. #--------------------------------------------------------------------- ## List channel modes supported by server. ## @Type API #--------------------------------------------------------------------- server_CHMODES_LISTMODES="b" #--------------------------------------------------------------------- ## "Always parameters" channel modes supported by server. ## @Type API #--------------------------------------------------------------------- server_CHMODES_ALWAYSPARAM="k" #--------------------------------------------------------------------- ## "Parameter on set" channel modes supported by server. ## @Type API #--------------------------------------------------------------------- server_CHMODES_PARAMONSET="l" #--------------------------------------------------------------------- ## Simple channel modes supported by server. ## @Type API #--------------------------------------------------------------------- server_CHMODES_SIMPLE="imnpst" #--------------------------------------------------------------------- ## Prefix channel modes supported by server. ## @Type API #--------------------------------------------------------------------- server_PREFIX_modes="ov" #--------------------------------------------------------------------- ## Channel prefixes supported by server. ## @Type API #--------------------------------------------------------------------- server_PREFIX_prefixes="@+" #--------------------------------------------------------------------- ## What is our current nick? ## @Type API #--------------------------------------------------------------------- server_nick_current="" #--------------------------------------------------------------------- ## 1 if we are connected, otherwise 0 ## @Type API #--------------------------------------------------------------------- server_connected=0 ########################################################################### # Internal functions to core or this file below this line! # # Module authors: go away # ########################################################################### #--------------------------------------------------------------------- ## Get some common data out of 005, the whole will also be saved to ## $server_005 for any module to use via parse_005(). ## This function is for cases that needs special action, like NAMESX ## and UHNAMES. ## This should be called directly after receiving a part of the 005! ## @Type Private ## @param The last part of the 005. #--------------------------------------------------------------------- server_handle_005() { # Example from freenode: # :heinlein.freenode.net 005 envbot IRCD=dancer CAPAB CHANTYPES=# EXCEPTS INVEX CHANMODES=bdeIq,k,lfJD,cgijLmnPQrRstz CHANLIMIT=#:20 PREFIX=(ov)@+ MAXLIST=bdeI:50 MODES=4 STATUSMSG=@ KNOCK NICKLEN=16 :are supported by this server # :heinlein.freenode.net 005 envbot SAFELIST CASEMAPPING=ascii CHANNELLEN=30 TOPICLEN=450 KICKLEN=450 KEYLEN=23 USERLEN=10 HOSTLEN=63 SILENCE=50 :are supported by this server local line="$1" if [[ $line =~ EXCEPTS(=([^ ]+))? ]]; then # Some, but not all also send what char the modes for EXCEPTS is. # If it isn't sent, lets guess it is +e if [[ ${BASH_REMATCH[2]} ]]; then server_EXCEPTS="${BASH_REMATCH[2]}" else server_EXCEPTS="e" fi fi if [[ $line =~ INVEX(=([^ ]+))? ]]; then # Some, but not all also send what char the modes for INVEX is. # If it isn't sent, lets guess it is +I if [[ ${BASH_REMATCH[2]} ]]; then server_INVEX="${BASH_REMATCH[2]}" else server_INVEX="I" fi fi if [[ $line =~ PREFIX=(\(([^ \)]+)\)([^ ]+)) ]]; then server_PREFIX="${BASH_REMATCH[1]}" server_PREFIX_modes="${BASH_REMATCH[2]}" server_PREFIX_prefixes="${BASH_REMATCH[3]}" fi if [[ $line =~ CHANMODES=([^ ,]+),([^ ,]+),([^ ,]+),([^ ,]+) ]]; then server_CHMODES_LISTMODES="${BASH_REMATCH[1]}" server_CHMODES_ALWAYSPARAM="${BASH_REMATCH[2]}" server_CHMODES_PARAMONSET="${BASH_REMATCH[3]}" server_CHMODES_SIMPLE="${BASH_REMATCH[4]}" fi # Enable NAMESX is supported. if [[ $line =~ NAMESX ]]; then log_info "Enabled NAMESX support" send_raw_flood "PROTOCTL NAMESX" server_NAMESX=1 fi # Enable UHNAMES if it is there. if [[ $line =~ UHNAMES ]]; then log_info "Enabled UHNAMES support" send_raw_flood "PROTOCTL UHNAMES" server_UHNAMES=1 fi } #--------------------------------------------------------------------- ## Respond to PING from server. ## @Type Private ## @param Raw line ## @return 0 If it was a PING ## @return 1 If it was not a PING #--------------------------------------------------------------------- server_handle_ping() { if [[ "$1" =~ ^PING\ *:(.*) ]] ;then send_raw "PONG :${BASH_REMATCH[1]}" return 0 fi return 1 } #--------------------------------------------------------------------- ## Handle numerics from server. ## @Type Private ## @param Numeric ## @param Target (self) ## @param Data #--------------------------------------------------------------------- server_handle_numerics() { # Slight sanity check if [[ "$2" != "$server_nick_current" ]]; then log_warning 'Own nick desynced!' log_warning "It should be $server_nick_current but server says it is $2" log_warning "Correcting own nick and lets hope that doesn't break anything" server_nick_current="$2" fi } #--------------------------------------------------------------------- ## Handle NICK messages from server ## @Type Private ## @param Sender ## @param New nick #--------------------------------------------------------------------- server_handle_nick() { local oldnick= parse_hostmask_nick "$1" 'oldnick' if [[ $oldnick == $server_nick_current ]]; then server_nick_current="$2" fi } #--------------------------------------------------------------------- ## Handle nick in use. ## @Type Private #--------------------------------------------------------------------- server_handle_nick_in_use() { if [[ $on_nick -eq 3 ]]; then log_error "Third nick is ALSO in use. I give up" bot_quit 2 elif [[ $on_nick -eq 2 ]]; then log_warning "Second nick is ALSO in use, trying third" send_nick "$config_thirdnick" server_nick_current="$config_thirdnick" on_nick=3 else log_info_stdout "First nick is in use, trying second" send_nick "$config_secondnick" on_nick=2 # FIXME: THIS IS HACKISH AND MAY BREAK server_nick_current="$config_secondnick" fi sleep 1 } #--------------------------------------------------------------------- ## Connect to IRC server. ## @Type Private #--------------------------------------------------------------------- server_connect() { server_connected=0 on_nick=1 # Clear current channels: channels_current="" # HACK: Clean up if we are aborted, replaced after connect with one that sends QUIT trap 'transport_disconnect; rm -rvf "$tmp_home"; exit 1' TERM INT log_info_stdout "Connecting to \"${config_server}:${config_server_port}\"..." transport_connect "$config_server" "$config_server_port" "$config_server_ssl" "$config_server_bind" || return 1 [[ $config_server_passwd ]] && send_raw_flood_nolog "PASS $config_server_passwd" log_info_stdout "logging in as $config_firstnick..." send_nick "$config_firstnick" # FIXME: THIS IS HACKISH AND MAY BREAK server_nick_current="$config_firstnick" # If a server password is set, send it. send_raw_flood "USER $config_ident 0 * :${config_gecos}" while true; do line= transport_read_line local transport_status="$?" # Still connected? if ! transport_alive; then return 1 fi # Did we timeout waiting for data # or did we get data? if [[ $transport_status -ne 0 ]]; then continue fi # Check with modules first, needed so we don't skip them. for module in $modules_on_connect; do module_${module}_on_connect "$line" done if [[ "$line" =~ ^:[^\ ]+\ +${numeric_RPL_MOTD} ]]; then continue fi log_raw_in "$line" if [[ "$line" =~ ^:[^\ ]+\ +([0-9]{3})\ +([^ ]+)\ +(.*) ]]; then local numeric="${BASH_REMATCH[1]}" # We use this to check below for our own nick. local numericnick="${BASH_REMATCH[2]}" local data="${BASH_REMATCH[3]}" case "$numeric" in "$numeric_RPL_MOTDSTART") log_info "Motd is not displayed in log"; ;; "$numeric_RPL_YOURHOST") if [[ $line =~ ^:([^ ]+) ]]; then # just to get the server name, this should always be true server_name="${BASH_REMATCH[1]}" fi ;; "$numeric_RPL_WELCOME") # This should work server_nick_current="$numericnick" ;; # We don't care about these and don't want to show it as unhandled. "$numeric_RPL_CREATED"|"$numeric_RPL_LUSERCLIENT"|"$numeric_RPL_LUSEROP"|"$numeric_RPL_LUSERUNKNOWN"|"$numeric_RPL_LUSERCHANNELS"|"$numeric_RPL_LUSERME"|"$numeric_RPL_LOCALUSERS"|"$numeric_RPL_GLOBALUSERS"|"$numeric_RPL_STATSCONN") continue ;; "$numeric_RPL_MYINFO") server_004="$data" server_004=$(tr -d $'\r\n' <<< "$server_004") # Get rid of ending newline ;; "$numeric_RPL_ISUPPORT") server_005+=" $data" server_005=$(tr -d $'\r\n' <<< "$server_005") # Get rid of newlines server_005="${server_005/ :are supported by this server/}" # Get rid of :are supported by this server server_handle_005 "$line" ;; "$numeric_ERR_NICKNAMEINUSE"|"$numeric_ERR_ERRONEUSNICKNAME") server_handle_nick_in_use ;; "$numeric_RPL_ENDOFMOTD"|"$numeric_ERR_NOMOTD") sleep 1 log_info_stdout 'Connected' server_connected=1 break ;; *) if [[ -z "${numerics[10#${numeric}]}" ]]; then log_info_file unknown_data.log "Unknown numeric during connect: $numeric Data: $data" else log_info_file unknown_data.log "Known but not handled numeric during connect: $numeric Data: $data" fi ;; esac fi server_handle_ping "$line" done }