From 9cbe149791ce49fda6003a3c0ff27fa3ea4216ef Mon Sep 17 00:00:00 2001 From: pk910 Date: Thu, 7 Jul 2011 21:24:53 +0200 Subject: [PATCH] added channel votes --- src/chanserv.c | 496 ++++++++++++++++++++++++++++++++++++++++++++++++- src/chanserv.h | 16 ++ 2 files changed, 509 insertions(+), 3 deletions(-) diff --git a/src/chanserv.c b/src/chanserv.c index b24f7c3..900bf85 100644 --- a/src/chanserv.c +++ b/src/chanserv.c @@ -112,6 +112,16 @@ #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" @@ -269,6 +279,7 @@ static const struct message_entry msgtab[] = { { "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" }, @@ -459,6 +470,32 @@ static const struct message_entry msgtab[] = { { "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", "%i: %s (%i votes)" }, + { "CSMSG_STARTVOTE_TOP", "%s has started a voting:" }, + { "CSMSG_STARTVOTE_QUESTION", "Question: %s" }, + { "CSMSG_STARTVOTE_OPTION", "%i: %s" }, + { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least %i access can vote." }, + { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use vote ID. To see the available options and the current votes, use vote 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 } @@ -624,7 +661,8 @@ static const struct { { "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 { @@ -864,6 +902,15 @@ chanserv_create_note_type(const char *name) 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) { @@ -1185,6 +1232,8 @@ register_channel(struct chanNode *cNode, char *registrar) channel->channel = cNode; LockChannel(cNode); cNode->channel_info = channel; + + channel->vote = NULL; return channel; } @@ -5891,6 +5940,11 @@ static MODCMD_FUNC(chan_opt_topicsnarf) 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); @@ -6382,6 +6436,362 @@ static MODCMD_FUNC(cmd_deleteme) 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)) { @@ -7232,7 +7642,7 @@ chanserv_conf_read(void) /* multiple choice options */ "CtcpReaction", "Protect", "Toys", "TopicRefresh", /* binary options */ - "DynLimit", "NoDelete", "expire", + "DynLimit", "NoDelete", "expire", "Vote", /* delimiter */ NULL }; @@ -7327,12 +7737,33 @@ chanserv_note_type_read(const char *key, struct record_data *rd) 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; @@ -7353,6 +7784,8 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan) 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) { @@ -7362,6 +7795,13 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan) 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 @@ -7591,6 +8031,20 @@ chanserv_channel_read(const char *key, struct record_data *hir) 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)) @@ -7717,6 +8171,10 @@ chanserv_write_users(struct saxdb_context *ctx, struct userData *uData) 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); @@ -7772,6 +8230,7 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel) int high_present; enum levelOption lvlOpt; enum charOption chOpt; + dict_iterator_t it; saxdb_start_record(ctx, channel->channel->name, 1); @@ -7793,6 +8252,27 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel) 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) @@ -8082,6 +8562,15 @@ init_chanserv(const char *nick) 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); @@ -8099,6 +8588,7 @@ init_chanserv(const char *nick) 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); diff --git a/src/chanserv.h b/src/chanserv.h index d6470e2..8c63e4a 100644 --- a/src/chanserv.h +++ b/src/chanserv.h @@ -45,6 +45,7 @@ enum levelOption { lvlUserInfo, lvlInviteMe, lvlTopicSnarf, + lvlVote, NUM_LEVEL_OPTIONS }; @@ -91,6 +92,10 @@ struct chanData char *registrar; char *topic_mask; + char *vote; + unsigned int vote_start; + dict_t vote_options; + unsigned int flags : 30; unsigned int may_opchan : 1; unsigned int max; @@ -129,6 +134,9 @@ struct userData unsigned int present : 1; unsigned int flags : USER_FLAGS_SIZE; + unsigned short voted; + unsigned int votefor; + /* linked list of userDatas for a chanData */ struct userData *prev; struct userData *next; @@ -164,6 +172,14 @@ struct suspended struct suspended *previous; }; +struct vote_option +{ + char *name; + unsigned int option_id; + char *option_str; + unsigned int voted; +}; + struct do_not_register { char chan_name[CHANNELLEN+1]; -- 2.20.1