Пример #1
0
/* Completely removes a channel.
 *
 * This includes the removal of all channel-bans, -exempts and -invites, as
 * well as all user flags related to the channel.
 */
void remove_channel(struct chanset_t *chan)
{
   if (chan != chanset_default) {
     irc_log(chan, "Parting");
     /* Remove the channel from the list, so that noone can pull it
        away from under our feet during the check_part() call. */
     chanset_unlink(chan);

    /* Using chan->name is important here, especially for !chans <cybah> */
    if (!conf.bot->hub && shouldjoin(chan) && chan->name[0])
      dprintf(DP_SERVER, "PART %s\n", chan->name);

     clear_channel(chan, 0);
     noshare = 1;
     /* Remove channel-bans */
     while (chan->bans)
       u_delmask('b', chan, chan->bans->mask, 1);
     /* Remove channel-exempts */
     while (chan->exempts)
       u_delmask('e', chan, chan->exempts->mask, 1);
     /* Remove channel-invites */
     while (chan->invites)
       u_delmask('I', chan, chan->invites->mask, 1);
     /* Remove channel specific user flags */
     user_del_chan(chan->dname);
     noshare = 0;
   }
   free(chan->channel.key);
   if (chan->key)
     free(chan->key);
   if (chan->rmkey)
     free(chan->rmkey);
   free(chan);
}
Пример #2
0
static void got_cjoin(char *botnick, char *code, char *par)
{
  if (!par[0])
   return;

  char *chname = newsplit(&par), *options = NULL;
  struct chanset_t *chan = findchan_by_dname(chname);
  int match = 0;

  if (conf.bot->hub) {
    newsplit(&par);	/* hubs ignore the botmatch param */
    options = par;
  } else {
    /* ALL hubs should add the channel, leaf should check the list for a match */
    bool inactive = 0;
    char *bots = newsplit(&par);
    match = parsebots(bots, conf.bot->nick);

    if (strstr(par, "+inactive"))
      inactive = 1;

    if (chan && !match)
      return;

    if (!match) {
      size_t size = strlen(par) + 12 + 1;

      options = (char *) my_calloc(1, size);
      simple_snprintf(options, size, "%s +inactive", par);
    } else if (match && chan && !shouldjoin(chan)) {
      if (!inactive)
        do_chanset(NULL, chan, "-inactive", DO_LOCAL);
      return;
    } else
      options = par;
  }

  if (chan)
    return;
sdprintf("OPTIONS: %s", options);
  char result[RESULT_LEN] = "";

  if (channel_add(result, chname, options) == ERROR) /* drummer */
    putlog(LOG_BOTS, "@", "Invalid channel or channel options from %s for %s: %s", botnick, chname, result);
  if (conf.bot->hub)
    write_userfile(-1);
  if (!match && !conf.bot->hub)
    free(options);
}
Пример #3
0
static void got_jn(int idx, char *code, char *par)
{
  char *chname = newsplit(&par);

  if (!chname || !chname[0]) 
    return;

  struct chanset_t *chan = NULL;

  if (!(chan = findchan_by_dname(chname))) return;
  if (chan->channel.jointime && channel_inactive(chan)) {
    chan->status &= ~CHAN_INACTIVE;
    chan->channel.jointime = 0;
    if (!conf.bot->hub && shouldjoin(chan))
      join_chan(chan);
  }
}
Пример #4
0
static int
check_slowjoinpart(struct chanset_t *chan)
{
  /* slowpart */
  if (chan->channel.parttime && (chan->channel.parttime < now)) {
    chan->channel.parttime = 0;
    dprintf(DP_MODE, "PART %s\n", chan->name);
    if (chan) /* this should NOT be necesary, but some unforseen bug requires it.. */
      remove_channel(chan);
    return 1;		/* if we keep looping, we'll segfault. */
  /* slowjoin */
  } else if ((chan->channel.jointime) && (chan->channel.jointime < now)) {
      chan->status &= ~CHAN_INACTIVE;
      chan->channel.jointime = 0;
    if (shouldjoin(chan))
      join_chan(chan);
  } else if (channel_closed(chan)) {
    enforce_closed(chan);
  }
  return 0;
}
Пример #5
0
void rcmd_chans(char *fbot, char *fhand, char *fidx) {
  if (conf.bot->hub)
    return;

  struct chanset_t *chan = NULL;
  char buf[1024] = "", reply[1024] = "";

  if (server_online) {
    for (chan = chanset; chan; chan = chan->next) {
      if (!channel_active(chan) && (shouldjoin(chan) || chan->channel.jointime)) {
        if (buf[0])
          strlcat(buf, " ", sizeof(buf));
        strlcat(buf, chan->dname, sizeof(buf));
      }
    }

    if (buf[0])
      simple_snprintf(reply, sizeof(reply), "[%s] I am not in: %s", cursrvname, buf);
  } else
    simple_snprintf(reply, sizeof(reply), "I am not online.");

  if (reply[0])
    botnet_send_cmdreply(conf.bot->nick, fbot, fhand, fidx, reply);
}
Пример #6
0
static void remote_tell_who(int idx, char *nick, int chan)
{
  int i = 10, k, l, ok = 0;
  char s[1024] = "", *realnick = NULL;
  struct chanset_t *c = NULL;

  realnick = strchr(nick, ':');
  if (realnick)
    realnick++;
  else
    realnick = nick;
  putlog(LOG_BOTS, "*", "#%s# who", realnick);
  strlcpy(s, "Channels: ", sizeof(s));
  for (c = chanset; c; c = c->next)
    if (!channel_secret(c) && shouldjoin(c)) {
      l = strlen(c->dname);
      if (i + l < 1021) {
	if (i > 10) {
          strlcat(s, ", ", sizeof(s));
          strlcat(s, c->dname, sizeof(s));
	} else {
          strlcpy(s, c->dname, sizeof(s));
	  i += (l + 2);
        }
      }
    }
  if (i > 10) {
    botnet_send_priv(idx, conf.bot->nick, nick, NULL, "%s  (%s)", s, ver);
  } else {
    botnet_send_priv(idx, conf.bot->nick, nick, NULL, "%s  (%s)", "no channels", ver);
  }
  if (admin[0])
    botnet_send_priv(idx, conf.bot->nick, nick, NULL, "Admin: %s", admin);
  if (chan == 0) {
    botnet_send_priv(idx, conf.bot->nick, nick, NULL,
		     "Party line members:  (^ = admin, * = owner, + = master, @ = op)");
  } else {
      botnet_send_priv(idx, conf.bot->nick, nick, NULL,
		       "People on channel %s%d:  (^ = admin, * = owner, + = master, @ = op)\n",
		       (chan < GLOBAL_CHANS) ? "" : "*",
		       chan % GLOBAL_CHANS);
  }
  for (i = 0; i < dcc_total; i++) {
    if (dcc[i].type && dcc[i].type->flags & DCT_REMOTEWHO) {
      if (dcc[i].u.chat->channel == chan) {
	k = simple_snprintf(s, sizeof(s), "  %c%-15s %s", (geticon(i) == '-' ? ' ' : geticon(i)),
		    dcc[i].nick, dcc[i].host);
	if (now - dcc[i].timeval > 300) {
	  unsigned long mydays, hrs, mins;

	  mydays = (now - dcc[i].timeval) / 86400;
	  hrs = ((now - dcc[i].timeval) - (mydays * 86400)) / 3600;
	  mins = ((now - dcc[i].timeval) - (hrs * 3600)) / 60;
	  if (mydays > 0)
	    simple_snprintf(s + k, sizeof(s) - k, " (idle %lud%luh)", mydays, hrs);
	  else if (hrs > 0)
	    simple_snprintf(s + k, sizeof(s) - k, " (idle %luh%lum)", hrs, mins);
	  else
	    simple_snprintf(s + k, sizeof(s) - k, " (idle %lum)", mins);
	}
	botnet_send_priv(idx, conf.bot->nick, nick, NULL, "%s", s);
	if (dcc[i].u.chat->away != NULL)
	  botnet_send_priv(idx, conf.bot->nick, nick, NULL, "      AWAY: %s", dcc[i].u.chat->away);
      }
    }
  }
  for (i = 0; i < dcc_total; i++) {
    if (dcc[i].type && dcc[i].type == &DCC_BOT) {
      if (!ok) {
	ok = 1;
	botnet_send_priv(idx, conf.bot->nick, nick, NULL, "%s:", "Bots connected");
      }
      simple_snprintf(s, sizeof(s), "  %s%c%-15s %s",
	      dcc[i].status & STAT_CALLED ? "<-" : "->",
	      dcc[i].status & STAT_SHARE ? '+' : ' ',
	      dcc[i].nick, dcc[i].u.bot->version);
      botnet_send_priv(idx, conf.bot->nick, nick, NULL, "%s", s);
    }
  }
  ok = 0;
  for (i = 0; i < dcc_total; i++) {
    if (dcc[i].type && dcc[i].type->flags & DCT_REMOTEWHO) {
      if (dcc[i].u.chat->channel != chan) {
	if (!ok) {
	  ok = 1;
	  botnet_send_priv(idx, conf.bot->nick, nick, NULL, "%s:", "Other people on the bot");
	}
	l = simple_snprintf(s, sizeof(s), "  %c%-15s %s", (geticon(i) == '-' ? ' ' : geticon(i)), dcc[i].nick, dcc[i].host);
	if (now - dcc[i].timeval > 300) {
	  k = (now - dcc[i].timeval) / 60;
	  if (k < 60)
	    simple_snprintf(s + l, sizeof(s) - l, " (idle %dm)", k);
	  else
	    simple_snprintf(s + l, sizeof(s) - l, " (idle %dh%dm)", k / 60, k % 60);
	}
	botnet_send_priv(idx, conf.bot->nick, nick, NULL, "%s", s);
	if (dcc[i].u.chat->away != NULL)
	  botnet_send_priv(idx, conf.bot->nick, nick, NULL, "      AWAY: %s", dcc[i].u.chat->away);
      }
    }
  }
}
Пример #7
0
void
restart(int idx)
{
  const char *reason = updating ? STR("Updating...") : STR("Restarting...");
  Tempfile *socks = new Tempfile("socks");
  int fd = 0;

  sdprintf("%s", reason); 

  if (tands > 0) {
    botnet_send_chat(-1, conf.bot->nick, (char *) reason);
    botnet_send_bye(reason);
  }

  /* kill all connections except STDOUT/server */
  for (fd = 0; fd < dcc_total; fd++) {
    if (dcc[fd].type && dcc[fd].type != &SERVER_SOCKET && dcc[fd].sock != STDOUT) {
      if (dcc[fd].sock >= 0)
        killsock(dcc[fd].sock);
      lostdcc(fd);
    }
  }

  const char salt1[] = SALT1;
  EncryptedStream stream(salt1);

  /* write out all leftover dcc[] entries */
  for (fd = 0; fd < dcc_total; fd++)
    if (dcc[fd].type && dcc[fd].sock != STDOUT)
      dcc_write(stream, fd);

  /* write out all leftover socklist[] entries */
  for (fd = 0; fd < MAXSOCKS; fd++)
    if (socklist[fd].sock != STDOUT)
      sock_write(stream, fd);

  if (server_online) {
    if (botname[0])
      stream << bd::String::printf(STR("+botname %s\n"), botname);
    if (rolls)
      stream << bd::String::printf(STR("+rolls %d\n"), rolls);
    if (altnick_char)
      stream << bd::String::printf(STR("+altnick_char %c\n"), altnick_char);
    if (burst)
      stream << bd::String::printf(STR("+burst %d\n"), burst);
    if (flood_count)
      stream << bd::String::printf(STR("+flood_count %d\n"), flood_count);
    if (my_cookie_counter)
      stream << bd::String::printf(STR("+my_cookie_counter %lu\n"), my_cookie_counter);
    stream << bd::String::printf(STR("+server_online %li\n"), (long)server_online);
  }
  stream << bd::String::printf(STR("+online_since %li\n"), (long)online_since);
  if (floodless)
    stream << bd::String::printf(STR("+server_floodless %d\n"), floodless);
  if (in_deaf)
    stream << bd::String::printf(STR("+in_deaf\n"));
  if (in_callerid)
    stream << bd::String::printf(STR("+in_callerid\n"));
  for (struct chanset_t *chan = chanset; chan; chan = chan->next)
    if (shouldjoin(chan) && (channel_active(chan) || channel_pending(chan)))
      stream << bd::String::printf(STR("+chan %s\n"), chan->dname);
  stream << bd::String::printf(STR("+buildts %li\n"), (long)buildts);
  stream << bd::String::printf(STR("+ip4 %s\n"), myipstr(AF_INET));
  stream << bd::String::printf(STR("+ip6 %s\n"), myipstr(AF_INET6));
  replay_cache(-1, &stream);

  stream.writeFile(socks->fd);

  socks->my_close();

  write_userfile(idx);
/*
  if (server_online) {
    do_chanset(NULL, NULL, STR("+inactive"), DO_LOCAL);
    dprintf(DP_DUMP, STR("JOIN 0\n"));
  }
*/
  fixmod(binname);

  /* replace image now */
  char *argv[4] = { NULL, NULL, NULL, NULL };

  argv[0] = strdup(binname);

  if (!backgrd || term_z || sdebug) {
    char shit[7] = "";

    simple_snprintf(shit, sizeof(shit), STR("-%s%s%s"), !backgrd ? "n" : "", term_z ? "t" : "", sdebug ? "D" : "");
    argv[1] = strdup(shit);
    argv[2] = strdup(conf.bot->nick);
  } else {
    argv[1] = strdup(conf.bot->nick);
  }

  unlink(conf.bot->pid_file);
  FILE *fp = NULL;
  if (!(fp = fopen(conf.bot->pid_file, "w")))
    return;
  fprintf(fp, "%d %s\n", getpid(), socks->file);
  fclose(fp);

  execvp(argv[0], &argv[0]);

  /* hopefully this is never reached */
  putlog(LOG_MISC, "*", STR("Could not restart: %s"), strerror(errno));
  return;
}
Пример #8
0
void show_channels(int idx, char *handle)
{
  struct userrec *u = NULL;
  size_t maxChannelLength = 0;
  bd::Array<bd::String> channelNames;
  bd::HashTable<bd::String, struct chanset_t*> channels;
  bd::String group;

  if (handle && handle[0] != '%') {
    u = get_user_by_handle(userlist, handle);
  } else {
    u = dcc[idx].user;
    if (handle && handle[0] == '%') {
      group = handle + 1;
    }
  }

  for (struct chanset_t* chan = chanset; chan; chan = chan->next) {
    struct flag_record fr = { FR_CHAN | FR_GLOBAL, 0, 0, 0 };
    const bd::String chname(chan->dname);
    // If a group was passed, ensure it matches
    if (group.length() && chan->groups->find(group) == chan->groups->npos) {
      continue;
    }
    get_user_flagrec(u, &fr, chan->dname);
    if (group.length() || real_chk_op(fr, chan, 0)) {
      if (maxChannelLength < chname.length()) {
        maxChannelLength = chname.length();
      }
      channelNames << chname;
      channels[chname] = chan;
    }
  }

  if (channelNames.length()) {
    char format[120] = "";
    simple_snprintf(format, sizeof(format), "  %%c%%-%zus %%-s%%-s%%-s%%-s%%-s%%-s\n", (maxChannelLength+2));
    if (group.length()) {
      dprintf(idx, "group '%s' is in %zu channel%s:\n", group.c_str(), channelNames.length(), (channelNames.length() > 1) ? "s" : "");
    } else {
      dprintf(idx, "%s %s access to %zu channel%s:\n", handle ? u->handle : "You", handle ? "has" : "have", channelNames.length(), (channelNames.length() > 1) ? "s" : "");
    }

    for (size_t i = 0; i < channelNames.length(); ++i) {
      const bd::String chname(channelNames[i]);
      const struct chanset_t* chan = channels[chname];
      dprintf(idx, format, !conf.bot->hub && me_op(chan) ? '@' : ' ', chan->dname, ((conf.bot->hub && channel_inactive(chan)) || (!conf.bot->hub && !shouldjoin(chan))) ? "(inactive) " : "",
          channel_privchan(chan) ? "(private)  " : "", chan->manop ? "(no manop) " : "", 
          channel_bitch(chan) && !channel_botbitch(chan) ? "(bitch)    " : channel_botbitch(chan) ? "(botbitch) " : "",
          channel_closed(chan) ?  "(closed) " : "", channel_backup(chan) ? "(backup)" : "");
    }
  } else {
    if (group.length()) {
      dprintf(idx, "No channels found for group '%s'\n", group.c_str());
    } else {
      dprintf(idx, "%s %s not have access to any channels.\n", handle ? u->handle : "You", handle ? "does" : "do");
    }
  }
}
Пример #9
0
void channels_report(int idx, int details)
{
  int i;
  char s[1024] = "", s2[100] = "";
  struct flag_record fr = {FR_CHAN | FR_GLOBAL, 0, 0, 0 };

  for (struct chanset_t *chan = chanset; chan; chan = chan->next) {
    if (idx != DP_STDOUT)
      get_user_flagrec(dcc[idx].user, &fr, chan->dname, chan);
    if (!privchan(fr, chan, PRIV_OP) && ((idx == DP_STDOUT) || glob_master(fr) || chan_master(fr))) {

      s[0] = 0;

      if (chan_bitch(chan))
	strlcat(s, "bitch, ", sizeof(s));
      if (s[0])
	s[strlen(s) - 2] = 0;
      if (!s[0])
	strlcpy(s, "lurking", sizeof(s));
      get_mode_protect(chan, s2, sizeof(s2));
      if (channel_closed(chan)) {
        if (chan->closed_invite)
          strlcat(s2, "i", sizeof(s2));
        if (chan->closed_private)
          strlcat(s2, "p", sizeof(s2));
      }

      if (shouldjoin(chan)) {
	if (channel_active(chan)) {
	  /* If it's a !chan, we want to display it's unique name too <cybah> */
	  if (chan->dname[0]=='!') {
	    dprintf(idx, "    %-20s: %2d member%s enforcing \"%s\" (%s), "
	            "unique name %s\n", chan->dname, chan->channel.members,
	            (chan->channel.members==1) ? "," : "s,", s2, s, chan->name);
	  } else {
	    dprintf(idx, "    %-20s: %2d member%s enforcing \"%s\" (%s)\n",
	            chan->dname, chan->channel.members,
	            chan->channel.members == 1 ? "," : "s,", s2, s);
	  }
	} else {
          if (!conf.bot->hub)
            dprintf(idx, "    %-20s: (%s), enforcing \"%s\"  (%s)\n", chan->dname,
		  channel_pending(chan) ? "pending" : "not on channel", s2, s);
          else
            dprintf(idx, "    %-20s: (%s), enforcing \"%s\"  (%s)\n", chan->dname,
		  "limbo", s2, s);
	}
      } else {
	dprintf(idx, "    %-20s: channel is set +inactive\n",
		chan->dname);
      }
      if (details) {
	s[0] = 0;
	i = 0;
	i += my_strcpy(s + i, "dynamic ");
	if (channel_enforcebans(chan))
	  i += my_strcpy(s + i, "enforcebans ");
	if (channel_dynamicbans(chan))
	  i += my_strcpy(s + i, "dynamicbans ");
	if (!channel_nouserbans(chan))
	  i += my_strcpy(s + i, "userbans ");
	if (channel_bitch(chan))
	  i += my_strcpy(s + i, "bitch ");
/*
	if (channel_revenge(chan))
	  i += my_strcpy(s + i, "revenge ");
	if (channel_revenge(chan))
	  i += my_strcpy(s + i, "revengebot ");
*/
	if (channel_secret(chan))
	  i += my_strcpy(s + i, "secret ");
	if (channel_cycle(chan))
	  i += my_strcpy(s + i, "cycle ");
	if (channel_dynamicexempts(chan))
	  i += my_strcpy(s + i, "dynamicexempts ");
	if (!channel_nouserexempts(chan))
	  i += my_strcpy(s + i, "userexempts ");
	if (channel_dynamicinvites(chan))
	  i += my_strcpy(s + i, "dynamicinvites ");
	if (!channel_nouserinvites(chan))
	  i += my_strcpy(s + i, "userinvites ");
	if (!shouldjoin(chan))
	  i += my_strcpy(s + i, "inactive ");
	if (channel_nodesynch(chan))
	  i += my_strcpy(s + i, "nodesynch ");
        if (channel_closed(chan))
          i += my_strcpy(s + i, "closed ");
        if (HAVE_TAKE && channel_take(chan))
          i += my_strcpy(s + i, "take ");
        if (channel_voice(chan))
          i += my_strcpy(s + i, "voice ");
        if (channel_autoop(chan))
          i += my_strcpy(s + i, "autoop ");
        if (channel_meankicks(chan))
          i += my_strcpy(s + i, "meankicks ");
        if (channel_rbl(chan))
          i += my_strcpy(s + i, "rbl ");
        if (channel_voicebitch(chan))
          i += my_strcpy(s + i, "voicebitch ");
        if (channel_protect(chan))
          i += my_strcpy(s + i, "protect ");
/* Chanflag template
 *	if (channel_temp(chan))
 *	  i += my_strcpy(s + i, "temp ");
*/
        if (channel_nomassjoin(chan))
          i += my_strcpy(s + i, "nomassjoin ");
        if (channel_botbitch(chan))
          i += my_strcpy(s + i, "botbitch ");
        if (channel_backup(chan))
          i += my_strcpy(s + i, "backup ");
        if (channel_fastop(chan))
          i += my_strcpy(s + i, "fastop ");
        if (channel_privchan(chan))
          i += my_strcpy(s + i, "private ");

	dprintf(idx, "      Options: %s\n", s);
        if (chan->limitraise)
          dprintf(idx, "      Raising limit +%d every 2 minutes\n", chan->limitraise);
/*
        if (chan->revenge_mode)
          dprintf(idx, "      revenge-mode %d\n",
                  chan->revenge_mode);
*/
       dprintf(idx, "    Bans last %d mins.\n", chan->ban_time);
       dprintf(idx, "    Exemptions last %d mins.\n", chan->exempt_time);
       dprintf(idx, "    Invitations last %d mins.\n", chan->invite_time);
      }
    }
  }
}
Пример #10
0
static void cmd_slowjoin(int idx, char *par)
{
  int intvl = 0, delay = 0, count = 0;
  char *chname = NULL, *p = NULL, buf[2048] = "", buf2[RESULT_LEN] = "";
  struct chanset_t *chan = NULL;
  tand_t *bot = NULL;

  /* slowjoin #chan 60 */
  putlog(LOG_CMDS, "*", "#%s# slowjoin %s", dcc[idx].nick, par);
  chname = newsplit(&par);
  p = newsplit(&par);
  intvl = atoi(p);
  if (!chname[0] || !p[0]) {
    dprintf(idx, "Usage: slowjoin <channel> <interval-seconds> [channel options]\n");
    return;
  }
  if (intvl < 10) {
    dprintf(idx, "Interval must be at least 10 seconds\n");
    return;
  }
  if ((chan = findchan_by_dname(chname))) {
    dprintf(idx, "Already on %s\n", chan->dname);
    return;
  }
  if (!strchr(CHANMETA, chname[0])) {
    dprintf(idx, "Invalid channel name\n");
    return;
  }

  simple_snprintf(buf, sizeof(buf), "+inactive addedby %s addedts %li ", dcc[idx].nick, (long)now);

  if (par[0])
    strlcat(buf, par, sizeof(buf));
  if (channel_add(buf2, chname, buf) == ERROR) {
    dprintf(idx, "Invalid channel or channel options.\n");
    if (buf2[0])
      dprintf(idx, " %s\n", buf2);
    return;
  }
  buf2[0] = 0;

  chan = findchan_by_dname(chname);
  if (!chan) {
    dprintf(idx, "Hmmm... Channel didn't get added. Weird *shrug*\n");
    return;
  }
  simple_snprintf(buf2, sizeof(buf2), "cjoin %s %s", chan->dname, buf);
  putallbots(buf2);
  if (conf.bot->hub)
    count = 0;

  chan->status &= ~CHAN_INACTIVE;

  for (bot = tandbot; bot; bot = bot->next) {
    char tmp[100] = "";
    tmp[0] = 0;    
    if (bot->u) {
      if (bot_hublevel(bot->u) < 999) {
	simple_snprintf(tmp, sizeof(tmp), "sj %s 0", chname);
      } else {
        struct flag_record fr = { FR_CHAN|FR_GLOBAL|FR_BOT, 0, 0, 0 };

        get_user_flagrec(bot->u, &fr, chname);
	/* Only send the 'sj' command if the bot is supposed to be in the channel (backups and such) */
        if (bot_shouldjoin(bot->u, &fr, chan, 1)) {
          /* Variation: 60 secs intvl should be 60 +/- 15 */
          int v = (random() % (intvl / 2)) - (intvl / 4);

          delay += intvl;
          simple_snprintf(tmp, sizeof(tmp), "sj %s %i", chname, delay + v);
          count++;
        }
      }
      if (tmp[0])
        putbot(bot->bot, tmp);
    }
  }

  if (!conf.bot->hub && shouldjoin(chan))
    count++;

  dprintf(idx, "%i bots joining %s during the next %i seconds\n", count, chan->dname, delay);

  if (!conf.bot->hub && shouldjoin(chan))
    join_chan(chan);
}