anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
cs_topic.cpp (8511B)
1 /* ChanServ core functions 2 * 3 * (C) 2003-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 * 8 * Based on the original code of Epona by Lara. 9 * Based on the original code of Services by Andy Church. 10 */ 11 12 #include "module.h" 13 #include "modules/cs_mode.h" 14 15 class CommandCSSetKeepTopic : public Command 16 { 17 public: 18 CommandCSSetKeepTopic(Module *creator, const Anope::string &cname = "chanserv/set/keeptopic") : Command(creator, cname, 2, 2) 19 { 20 this->SetDesc(_("Retain topic when channel is not in use")); 21 this->SetSyntax(_("\037channel\037 {ON | OFF}")); 22 } 23 24 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 25 { 26 if (Anope::ReadOnly) 27 { 28 source.Reply(READ_ONLY_MODE); 29 return; 30 } 31 32 ChannelInfo *ci = ChannelInfo::Find(params[0]); 33 if (ci == NULL) 34 { 35 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); 36 return; 37 } 38 39 EventReturn MOD_RESULT; 40 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1])); 41 if (MOD_RESULT == EVENT_STOP) 42 return; 43 44 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration")) 45 { 46 source.Reply(ACCESS_DENIED); 47 return; 48 } 49 50 if (params[1].equals_ci("ON")) 51 { 52 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keeptopic"; 53 ci->Extend<bool>("KEEPTOPIC"); 54 source.Reply(_("Topic retention option for %s is now \002on\002."), ci->name.c_str()); 55 } 56 else if (params[1].equals_ci("OFF")) 57 { 58 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keeptopic"; 59 ci->Shrink<bool>("KEEPTOPIC"); 60 source.Reply(_("Topic retention option for %s is now \002off\002."), ci->name.c_str()); 61 } 62 else 63 this->OnSyntaxError(source, "KEEPTOPIC"); 64 } 65 66 bool OnHelp(CommandSource &source, const Anope::string &) anope_override 67 { 68 this->SendSyntax(source); 69 source.Reply(" "); 70 source.Reply(_("Enables or disables the \002topic retention\002 option for a\n" 71 "channel. When \002%s\002 is set, the topic for the\n" 72 "channel will be remembered by %s even after the\n" 73 "last user leaves the channel, and will be restored the\n" 74 "next time the channel is created."), source.command.c_str(), source.service->nick.c_str()); 75 return true; 76 } 77 }; 78 79 class CommandCSTopic : public Command 80 { 81 ExtensibleRef<bool> topiclock; 82 83 void Lock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 84 { 85 if (Anope::ReadOnly) 86 { 87 source.Reply(READ_ONLY_MODE); 88 return; 89 } 90 91 EventReturn MOD_RESULT; 92 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, "topiclock on")); 93 if (MOD_RESULT == EVENT_STOP) 94 return; 95 96 topiclock->Set(ci, true); 97 source.Reply(_("Topic lock option for %s is now \002on\002."), ci->name.c_str()); 98 } 99 100 void Unlock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 101 { 102 if (Anope::ReadOnly) 103 { 104 source.Reply(READ_ONLY_MODE); 105 return; 106 } 107 108 EventReturn MOD_RESULT; 109 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, "topiclock off")); 110 if (MOD_RESULT == EVENT_STOP) 111 return; 112 113 topiclock->Unset(ci); 114 source.Reply(_("Topic lock option for %s is now \002off\002."), ci->name.c_str()); 115 } 116 117 void Set(CommandSource &source, ChannelInfo *ci, const Anope::string &topic) 118 { 119 bool has_topiclock = topiclock->HasExt(ci); 120 topiclock->Unset(ci); 121 ci->c->ChangeTopic(source.GetNick(), topic, Anope::CurTime); 122 if (has_topiclock) 123 topiclock->Set(ci); 124 125 bool override = !source.AccessFor(ci).HasPriv("TOPIC"); 126 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << (!topic.empty() ? "to change the topic to: " : "to unset the topic") << (!topic.empty() ? topic : ""); 127 } 128 129 void Append(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 130 { 131 const Anope::string &topic = params[2]; 132 133 Anope::string new_topic; 134 if (!ci->c->topic.empty()) 135 { 136 new_topic = ci->c->topic + " " + topic; 137 ci->last_topic.clear(); 138 } 139 else 140 new_topic = topic; 141 142 this->Set(source, ci, new_topic); 143 } 144 145 public: 146 CommandCSTopic(Module *creator) : Command(creator, "chanserv/topic", 2, 3), 147 topiclock("TOPICLOCK") 148 { 149 this->SetDesc(_("Manipulate the topic of the specified channel")); 150 this->SetSyntax(_("\037channel\037 [SET] [\037topic\037]")); 151 this->SetSyntax(_("\037channel\037 APPEND \037topic\037")); 152 this->SetSyntax(_("\037channel\037 [UNLOCK|LOCK]")); 153 } 154 155 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 156 { 157 const Anope::string &subcmd = params[1]; 158 159 ChannelInfo *ci = ChannelInfo::Find(params[0]); 160 if (ci == NULL) 161 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); 162 else if (!source.AccessFor(ci).HasPriv("TOPIC") && !source.HasCommand("chanserv/topic")) 163 source.Reply(ACCESS_DENIED); 164 else if (subcmd.equals_ci("LOCK")) 165 this->Lock(source, ci, params); 166 else if (subcmd.equals_ci("UNLOCK")) 167 this->Unlock(source, ci, params); 168 else if (!ci->c) 169 source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); 170 else if (subcmd.equals_ci("APPEND") && params.size() > 2) 171 this->Append(source, ci, params); 172 else 173 { 174 Anope::string topic; 175 if (subcmd.equals_ci("SET")) 176 { 177 topic = params.size() > 2 ? params[2] : ""; 178 } 179 else 180 { 181 topic = subcmd; 182 if (params.size() > 2) 183 topic += " " + params[2]; 184 } 185 this->Set(source, ci, topic); 186 } 187 } 188 189 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 190 { 191 this->SendSyntax(source); 192 source.Reply(" "); 193 source.Reply(_("Allows manipulating the topic of the specified channel.\n" 194 "The \002SET\002 command changes the topic of the channel to the given topic\n" 195 "or unsets the topic if no topic is given. The \002APPEND\002 command appends\n" 196 "the given topic to the existing topic.\n" 197 " \n" 198 "\002LOCK\002 and \002UNLOCK\002 may be used to enable and disable topic lock. When\n" 199 "topic lock is set, the channel topic will be unchangeable by users who do not have\n" 200 "the \002TOPIC\002 privilege.")); 201 return true; 202 } 203 }; 204 205 class CSTopic : public Module 206 { 207 CommandCSTopic commandcstopic; 208 CommandCSSetKeepTopic commandcssetkeeptopic; 209 210 SerializableExtensibleItem<bool> topiclock, keeptopic; 211 212 public: 213 CSTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 214 commandcstopic(this), commandcssetkeeptopic(this), topiclock(this, "TOPICLOCK"), keeptopic(this, "KEEPTOPIC") 215 { 216 217 } 218 219 void OnChannelSync(Channel *c) anope_override 220 { 221 if (c->ci) 222 { 223 /* Update channel topic */ 224 if ((topiclock.HasExt(c->ci) || keeptopic.HasExt(c->ci)) && c->ci->last_topic != c->topic) 225 { 226 c->ChangeTopic(!c->ci->last_topic_setter.empty() ? c->ci->last_topic_setter : c->ci->WhoSends()->nick, c->ci->last_topic, c->ci->last_topic_time ? c->ci->last_topic_time : Anope::CurTime); 227 } 228 } 229 } 230 231 void OnTopicUpdated(User *source, Channel *c, const Anope::string &user, const Anope::string &topic) anope_override 232 { 233 if (!c->ci) 234 return; 235 236 /* We only compare the topics here, not the time or setter. This is because some (old) IRCds do not 237 * allow us to set the topic as someone else, meaning we have to bump the TS and change the setter to us. 238 * This desyncs what is really set with what we have stored, and we end up resetting the topic often when 239 * it is not required 240 */ 241 if (topiclock.HasExt(c->ci) && c->ci->last_topic != c->topic && (!source || !c->ci->AccessFor(source).HasPriv("TOPIC"))) 242 { 243 c->ChangeTopic(c->ci->last_topic_setter, c->ci->last_topic, c->ci->last_topic_time); 244 } 245 else 246 { 247 c->ci->last_topic = c->topic; 248 c->ci->last_topic_setter = c->topic_setter; 249 c->ci->last_topic_time = c->topic_ts; 250 } 251 } 252 253 void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override 254 { 255 if (keeptopic.HasExt(ci)) 256 info.AddOption(_("Topic retention")); 257 if (topiclock.HasExt(ci)) 258 info.AddOption(_("Topic lock")); 259 260 ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks"); 261 const ModeLock *secret = ml ? ml->GetMLock("SECRET") : NULL; 262 if (!ci->last_topic.empty() && (show_all || ((!secret || secret->set == false) && (!ci->c || !ci->c->HasMode("SECRET"))))) 263 { 264 info[_("Last topic")] = ci->last_topic; 265 info[_("Topic set by")] = ci->last_topic_setter; 266 } 267 } 268 }; 269 270 MODULE_INIT(CSTopic)