added channel votes
authorpk910 <philipp@zoelle1.de>
Thu, 7 Jul 2011 19:24:53 +0000 (21:24 +0200)
committerpk910 <philipp@zoelle1.de>
Thu, 7 Jul 2011 19:24:53 +0000 (21:24 +0200)
src/chanserv.c
src/chanserv.h

index b24f7c3c4a9f1b468f171483349aa32e254edfa2..900bf854fb812503736b65de34e9daaa527dd1d0 100644 (file)
 #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", "\ 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 }
@@ -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);
index d6470e2455c653a4d32a0eaa151204932c7d3008..8c63e4a5f9ea57a46277b99c96e673eff185f671 100644 (file)
@@ -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];