#define KEY_INFO "info"
#define KEY_SEEN "seen"
+/* Votes */
+#define KEY_VOTE "vote"
+#define KEY_VOTE_START "votestart"
+#define KEY_VOTE_OPTIONS "voptions"
+#define KEY_VOTE_OPTION_NAME "voptionname"
+#define KEY_VOTE_VOTED "vvoted"
+#define KEY_VOTE_VOTEDFOR "vvotefor"
+#define KEY_VOTE_OPTION_ID "voptionid"
+#define KEY_VOTE_OPTION_VOTED "voptionvoted"
+
/* Ban data */
#define KEY_OWNER "owner"
#define KEY_REASON "reason"
{ "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
{ "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
{ "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
+ { "CSMSG_SET_VOTE", "$bVote $b %d" },
{ "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
{ "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
{ "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
{ "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
{ "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
+/* Vote */
+ { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
+ { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
+ { "CSMSG_DELVOTE_DONE", "Vote deleted." },
+ { "CSMSG_NO_VOTE", "There is no vote in this channel." },
+ { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
+ { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
+ { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
+ { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
+ { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
+ { "CSMSG_VOTE_QUESTION", "Question: %s" },
+ { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
+ { "CSMSG_VOTERES_QUESTION", "Question: %s" },
+ { "CSMSG_VOTERES_OPTION", "\ 2%i\ 2: %s (\ 2%i\ 2 votes)" },
+ { "CSMSG_STARTVOTE_TOP", "\ 2%s\ 2 has started a voting:" },
+ { "CSMSG_STARTVOTE_QUESTION", "\ 2Question:\ 2 %s" },
+ { "CSMSG_STARTVOTE_OPTION", "\ 2%i:\ 2 %s" },
+ { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least \ 2%i\ 2 access can vote." },
+ { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use \ 2vote ID\ 2. To see the available options and the current votes, use \ 2vote\ 2 without parameters." },
+ { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
+ { "CSMSG_VOTE_VOTED", "You have already voted." },
+ { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
+ { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
+ { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
+ { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
+
/* Other things */
{ "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
{ NULL, NULL }
{ "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
{ "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
{ "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
- { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
+ { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
+ { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
};
struct charOptionValues {
return ntype;
}
+static void
+free_vote_options(void *data)
+{
+ struct vote_option *vOpt = data;
+ free(vOpt->name);
+ free(vOpt->option_str);
+ free(vOpt);
+}
+
static void
chanserv_deref_note_type(void *data)
{
channel->channel = cNode;
LockChannel(cNode);
cNode->channel_info = channel;
+
+ channel->vote = NULL;
return channel;
}
return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
}
+static MODCMD_FUNC(chan_opt_vote)
+{
+ return channel_level_option(lvlVote, CSFUNC_ARGS);
+}
+
static MODCMD_FUNC(chan_opt_inviteme)
{
return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
return 1;
}
+static CHANSERV_FUNC(cmd_addvote)
+{
+ struct chanData *cData = channel->channel_info;
+ struct userData *uData, *target;
+ struct handle_info *hi;
+ if (!cData) return 0;
+ REQUIRE_PARAMS(2);
+ hi = user->handle_info;
+ if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
+ {
+ reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
+ return 0;
+ }
+ if(target->access < 300) {
+ reply("CSMSG_NO_ACCESS");
+ return 0;
+ }
+ if (cData->vote) {
+ reply("CSMSG_ADDVOTE_FULL");
+ return 0;
+ }
+ char *msg;
+ msg = unsplit_string(argv + 1, argc - 1, NULL);
+ cData->vote = strdup(msg);
+ cData->vote_start=0;
+ dict_delete(cData->vote_options);
+ cData->vote_options = dict_new();
+ dict_set_free_data(cData->vote_options, free_vote_options);
+ for(uData = channel->channel_info->users; uData; uData = uData->next)
+ {
+ uData->voted = 0;
+ uData->votefor = 0;
+ }
+ reply("CSMSG_ADDVOTE_DONE");
+ return 1;
+}
+
+static CHANSERV_FUNC(cmd_delvote)
+{
+ struct chanData *cData = channel->channel_info;
+ struct userData *target;
+ struct handle_info *hi;
+ if (!cData) return 0;
+ hi = user->handle_info;
+ if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
+ {
+ reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
+ return 0;
+ }
+ if(target->access < 300) {
+ reply("CSMSG_NO_ACCESS");
+ return 0;
+ }
+ if (!cData->vote) {
+ reply("CSMSG_NO_VOTE");
+ return 0;
+ }
+ free(cData->vote);
+ cData->vote = NULL;
+ reply("CSMSG_DELVOTE_DONE");
+ return 1;
+}
+
+static CHANSERV_FUNC(cmd_addoption)
+{
+ struct chanData *cData = channel->channel_info;
+ struct userData *target;
+ struct handle_info *hi;
+ if (!cData) return 0;
+ REQUIRE_PARAMS(2);
+ hi = user->handle_info;
+ if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
+ {
+ reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
+ return 0;
+ }
+ if(target->access < 300) {
+ reply("CSMSG_NO_ACCESS");
+ return 0;
+ }
+ if (!cData->vote) {
+ reply("CSMSG_NO_VOTE");
+ return 0;
+ }
+
+ char *msg;
+
+ msg = unsplit_string(argv + 1, argc - 1, NULL);
+
+ dict_iterator_t it;
+ unsigned int lastid = 1;
+ for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
+ struct vote_option *cvOpt = iter_data(it);
+ if(cvOpt->option_id > lastid)
+ lastid = cvOpt->option_id;
+ }
+ struct vote_option *vOpt;
+ vOpt = calloc(1, sizeof(*vOpt));
+ vOpt->name = strdup(msg);
+ vOpt->option_id = (lastid + 1);
+ char str[50];
+ sprintf(str,"%i",(lastid + 1));
+ vOpt->option_str = strdup(str);
+ vOpt->voted = 0;
+ dict_insert(cData->vote_options,vOpt->option_str,vOpt);
+
+ reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
+ return 1;
+}
+
+static CHANSERV_FUNC(cmd_deloption)
+{
+ struct chanData *cData = channel->channel_info;
+ struct userData *uData, *target;
+ struct handle_info *hi;
+ if (!cData) return 0;
+ REQUIRE_PARAMS(2);
+ hi = user->handle_info;
+ if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
+ {
+ reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
+ return 0;
+ }
+ if(target->access < 300) {
+ reply("CSMSG_NO_ACCESS");
+ return 0;
+ }
+ if (!cData->vote) {
+ reply("CSMSG_NO_VOTE");
+ return 0;
+ }
+ if(cData->vote_start) {
+ if(dict_size(cData->vote_options) < 3) {
+ reply("CSMSG_VOTE_NEED_OPTIONS");
+ return 0;
+ }
+ }
+
+ int find_id = atoi(argv[1]);
+ int ii = 0;
+ unsigned int found = 0;
+ dict_iterator_t it;
+
+ for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
+ ii++;
+ if (find_id == ii) {
+ struct vote_option *vOpt = iter_data(it);
+ found = vOpt->option_id;
+ char str[50];
+ sprintf(str,"%i",vOpt->option_id);
+ dict_remove(cData->vote_options, str);
+ }
+ }
+
+ if(found > 0) {
+ for(uData = channel->channel_info->users; uData; uData = uData->next) {
+ if(uData->votefor == found) {
+ uData->voted = 0;
+ uData->votefor = 0;
+ }
+ }
+ reply("CSMSG_DELOPTION_DONE");
+ return 1;
+ } else {
+ reply("CSMSG_DELOPTION_NONE");
+ return 0;
+ }
+}
+
+static CHANSERV_FUNC(cmd_vote)
+{
+ struct chanData *cData = channel->channel_info;
+ struct userData *target;
+ struct handle_info *hi;
+ unsigned int votedfor = 0;
+ char *votedfor_str;
+
+ if (!cData || !cData->vote) {
+ reply("CSMSG_NO_VOTE");
+ return 0;
+ }
+ if(argc > 1 && cData->vote_start) {
+ hi = user->handle_info;
+ if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
+ {
+ reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
+ return 0;
+ }
+ if(!check_user_level(channel, user, lvlVote, 1, 0)) {
+ reply("CSMSG_NO_ACCESS");
+ return 0;
+ }
+ if(target->voted) {
+ reply("CSMSG_VOTE_VOTED");
+ return 0;
+ }
+ int find_id = atoi(argv[1]);
+ int ii = 0;
+ dict_iterator_t it;
+ for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
+ ii++;
+ if (find_id == ii) {
+ struct vote_option *vOpt = iter_data(it);
+ vOpt->voted++;
+ target->voted = 1;
+ target->votefor = vOpt->option_id;
+ votedfor = vOpt->option_id;
+ votedfor_str = vOpt->name;
+ }
+ }
+ if(votedfor == 0) {
+ reply("CSMSG_VOTE_INVALID");
+ return 0;
+ }
+ }
+ if (!cData->vote_start) {
+ reply("CSMSG_VOTE_NOT_STARTED");
+ }
+ reply("CSMSG_VOTE_QUESTION",cData->vote);
+
+ unsigned int voteid = 0;
+ dict_iterator_t it;
+
+ for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
+ struct vote_option *vOpt = iter_data(it);
+ voteid++;
+ reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
+ }
+ if(argc > 1 && cData->vote_start && votedfor_str) {
+ reply("CSMSG_VOTE_DONE",votedfor_str);
+ }
+ return 1;
+}
+
+static CHANSERV_FUNC(cmd_startvote)
+{
+ struct chanData *cData = channel->channel_info;
+ struct userData *target;
+ struct handle_info *hi;
+ if (!cData) return 0;
+ hi = user->handle_info;
+ if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
+ {
+ reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
+ return 0;
+ }
+ if(target->access < 300) {
+ reply("CSMSG_NO_ACCESS");
+ return 0;
+ }
+ if (!cData->vote) {
+ reply("CSMSG_NO_VOTE");
+ return 0;
+ }
+ if(cData->vote_start) {
+ reply("CSMSG_STARTVOTE_RUNNING");
+ return 0;
+ }
+ if(dict_size(cData->vote_options) < 2) {
+ reply("CSMSG_VOTE_NEED_OPTIONS");
+ return 0;
+ }
+ cData->vote_start = 1;
+ char response[MAXLEN];
+ sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
+ irc_privmsg(cmd->parent->bot, channel->name, response);
+ sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
+ irc_privmsg(cmd->parent->bot, channel->name, response);
+ unsigned int voteid = 0;
+ dict_iterator_t it;
+ for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
+ struct vote_option *vOpt = iter_data(it);
+ voteid++;
+ sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
+ irc_privmsg(cmd->parent->bot, channel->name, response);
+ }
+ sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
+ irc_privmsg(cmd->parent->bot, channel->name, response);
+ sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
+ irc_privmsg(cmd->parent->bot, channel->name, response);
+}
+
+static CHANSERV_FUNC(cmd_endvote)
+{
+ struct chanData *cData = channel->channel_info;
+ struct userData *target;
+ struct handle_info *hi;
+ if (!cData) return 0;
+ hi = user->handle_info;
+ if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
+ {
+ reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
+ return 0;
+ }
+ if(target->access < 300) {
+ reply("CSMSG_NO_ACCESS");
+ return 0;
+ }
+ if (!cData->vote) {
+ reply("CSMSG_NO_VOTE");
+ return 0;
+ }
+ if(!cData->vote_start) {
+ reply("CSMSG_ENDVOTE_STOPPED");
+ return 0;
+ }
+ cData->vote_start = 0;
+ reply("CSMSG_ENDVOTE_DONE");
+ return 1;
+}
+
+static CHANSERV_FUNC(cmd_voteresults)
+{
+ struct chanData *cData = channel->channel_info;
+ struct userData *target;
+ struct handle_info *hi;
+ if (!cData) return 0;
+ if (!cData->vote) {
+ reply("CSMSG_NO_VOTE");
+ return 0;
+ }
+ if (argc > 1 && !irccasecmp(argv[1], "*")) {
+ hi = user->handle_info;
+ if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
+ {
+ reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
+ return 0;
+ }
+ if(target->access < 300) {
+ reply("CSMSG_NO_ACCESS");
+ return 0;
+ }
+ char response[MAXLEN];
+ sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
+ irc_privmsg(cmd->parent->bot, channel->name, response);
+ unsigned int voteid = 0;
+ dict_iterator_t it;
+ for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
+ struct vote_option *vOpt = iter_data(it);
+ voteid++;
+ sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
+ irc_privmsg(cmd->parent->bot, channel->name, response);
+ }
+ } else {
+ reply("CSMSG_VOTE_QUESTION",cData->vote);
+ unsigned int voteid = 0;
+ dict_iterator_t it;
+ for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
+ struct vote_option *vOpt = iter_data(it);
+ voteid++;
+ reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
+ }
+ }
+ return 1;
+}
+
static void
chanserv_refresh_topics(UNUSED_ARG(void *data))
{
/* multiple choice options */
"CtcpReaction", "Protect", "Toys", "TopicRefresh",
/* binary options */
- "DynLimit", "NoDelete", "expire",
+ "DynLimit", "NoDelete", "expire", "Vote",
/* delimiter */
NULL
};
ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
}
+static void
+vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
+{
+ struct vote_option *vOpt;
+ char *str;
+
+ if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
+ {
+ log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
+ return;
+ }
+
+ vOpt = calloc(1, sizeof(*vOpt));
+ vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
+ str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
+ vOpt->voted = str ? atoi(str) : 0;
+ vOpt->option_id = str ? atoi(key) : 0;
+ vOpt->option_str = strdup(key);
+ dict_insert(chan->vote_options,vOpt->option_str,vOpt);
+}
+
static void
user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
{
struct handle_info *handle;
struct userData *uData;
- char *seen, *inf, *flags;
+ char *seen, *inf, *flags, *voted, *votefor;
unsigned long last_seen;
unsigned short access_level;
seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
last_seen = seen ? strtoul(seen, NULL, 0) : now;
flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
+ voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
+ votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
handle = get_handle_info(key);
if(!handle)
{
uData = add_channel_user(chan, handle, access_level, last_seen, inf);
uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
+ if(chan->vote) {
+ uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
+ uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
+ } else {
+ uData->voted = 0;
+ uData->votefor = 0;
+ }
}
static void
str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
cData->topic = str ? strdup(str) : NULL;
+ str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
+ if(str) {
+ cData->vote = str ? strdup(str) : NULL;
+ dict_delete(cData->vote_options);
+ cData->vote_options = dict_new();
+ dict_set_free_data(cData->vote_options, free_vote_options);
+ str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
+ cData->vote_start = str ? atoi(str) : 0;
+ obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
+ for(it = dict_first(obj); it; it = iter_next(it)) {
+ vote_option_read_helper(iter_key(it), iter_data(it), cData);
+ }
+ }
+
if(!IsSuspended(cData)
&& (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
&& (argc = split_line(str, 0, ArrayLength(argv), argv))
saxdb_write_int(ctx, KEY_SEEN, uData->seen);
if(uData->flags)
saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
+ if(uData->channel->vote && uData->voted)
+ saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
+ if(uData->channel->vote && uData->votefor)
+ saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
if(uData->info)
saxdb_write_string(ctx, KEY_INFO, uData->info);
saxdb_end_record(ctx);
int high_present;
enum levelOption lvlOpt;
enum charOption chOpt;
+ dict_iterator_t it;
saxdb_start_record(ctx, channel->channel->name, 1);
if(channel->expiry)
saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
+ if(channel->vote) {
+ saxdb_write_string(ctx, KEY_VOTE, channel->vote);
+ if(channel->vote_start)
+ saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
+ if (dict_size(channel->vote_options)) {
+ saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
+ for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
+ struct vote_option *vOpt = iter_data(it);
+ char str[50];
+ sprintf(str,"%i",vOpt->option_id);
+ saxdb_start_record(ctx, str, 0);
+ if(vOpt->voted)
+ saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
+ if(vOpt->name)
+ saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
+ saxdb_end_record(ctx);
+ }
+ saxdb_end_record(ctx);
+ }
+ }
+
saxdb_start_record(ctx, KEY_OPTIONS, 0);
saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+
+ DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
+ DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
+ DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
+ DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
+ DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
+ DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
+ DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
+ DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
/* Channel options */
DEFINE_CHANNEL_OPTION(defaulttopic);
DEFINE_CHANNEL_OPTION(userinfo);
DEFINE_CHANNEL_OPTION(dynlimit);
DEFINE_CHANNEL_OPTION(topicsnarf);
+ DEFINE_CHANNEL_OPTION(vote);
DEFINE_CHANNEL_OPTION(nodelete);
DEFINE_CHANNEL_OPTION(toys);
DEFINE_CHANNEL_OPTION(setters);