00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "module.h"
00015 #include "global.h"
00016 #include "os_news.h"
00017
00018
00019
00020 enum
00021 {
00022 MSG_SYNTAX,
00023 MSG_LIST_HEADER,
00024 MSG_LIST_NONE,
00025 MSG_ADDED,
00026 MSG_DEL_NOT_FOUND,
00027 MSG_DELETED,
00028 MSG_DEL_NONE,
00029 MSG_DELETED_ALL
00030 };
00031
00032 struct NewsMessages msgarray[] = {
00033 {NEWS_LOGON, "LOGON",
00034 {_("LOGONNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
00035 _("Logon news items:"),
00036 _("There is no logon news."),
00037 _("Added new logon news item."),
00038 _("Logon news item #%s not found!"),
00039 _("Logon news item #%d deleted."),
00040 _("No logon news items to delete!"),
00041 _("All logon news items deleted.")}
00042 },
00043 {NEWS_OPER, "OPER",
00044 {_("OPERNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
00045 _("Oper news items:"),
00046 _("There is no oper news."),
00047 _("Added new oper news item."),
00048 _("Oper news item #%s not found!"),
00049 _("Oper news item #%d deleted."),
00050 _("No oper news items to delete!"),
00051 _("All oper news items deleted.")}
00052 },
00053 {NEWS_RANDOM, "RANDOM",
00054 {_("RANDOMNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
00055 _("Random news items:"),
00056 _("There is no random news."),
00057 _("Added new random news item."),
00058 _("Random news item #%s not found!"),
00059 _("Random news item #%d deleted."),
00060 _("No random news items to delete!"),
00061 _("All random news items deleted.")}
00062 }
00063 };
00064
00065 class MyNewsService : public NewsService
00066 {
00067 std::vector<NewsItem *> newsItems[3];
00068 public:
00069 MyNewsService(Module *m) : NewsService(m) { }
00070
00071 ~MyNewsService()
00072 {
00073 for (unsigned i = 0; i < 3; ++i)
00074 for (unsigned j = 0; j < newsItems[i].size(); ++j)
00075 delete newsItems[i][j];
00076 }
00077
00078 void AddNewsItem(NewsItem *n)
00079 {
00080 this->newsItems[n->type].push_back(n);
00081 }
00082
00083 void DelNewsItem(NewsItem *n)
00084 {
00085 std::vector<NewsItem *> &list = this->GetNewsList(n->type);
00086 std::vector<NewsItem *>::iterator it = std::find(list.begin(), list.end(), n);
00087 if (it != list.end())
00088 list.erase(it);
00089 delete n;
00090 }
00091
00092 std::vector<NewsItem *> &GetNewsList(NewsType t)
00093 {
00094 return this->newsItems[t];
00095 }
00096 };
00097
00098 #define lenof(a) (sizeof(a) / sizeof(*(a)))
00099 static const char **findmsgs(NewsType type)
00100 {
00101 for (unsigned i = 0; i < lenof(msgarray); ++i)
00102 if (msgarray[i].type == type)
00103 return msgarray[i].msgs;
00104 return NULL;
00105 }
00106
00107 class NewsBase : public Command
00108 {
00109 ServiceReference<NewsService> ns;
00110
00111 protected:
00112 void DoList(CommandSource &source, NewsType ntype, const char **msgs)
00113 {
00114 std::vector<NewsItem *> &list = this->ns->GetNewsList(ntype);
00115 if (list.empty())
00116 source.Reply(msgs[MSG_LIST_NONE]);
00117 else
00118 {
00119 ListFormatter lflist;
00120 lflist.AddColumn("Number").AddColumn("Creator").AddColumn("Created").AddColumn("Text");
00121
00122 for (unsigned i = 0, end = list.size(); i < end; ++i)
00123 {
00124 ListFormatter::ListEntry entry;
00125 entry["Number"] = stringify(i + 1);
00126 entry["Creator"] = list[i]->who;
00127 entry["Created"] = Anope::strftime(list[i]->time);
00128 entry["Text"] = list[i]->text;
00129 lflist.AddEntry(entry);
00130 }
00131
00132 source.Reply(msgs[MSG_LIST_HEADER]);
00133
00134 std::vector<Anope::string> replies;
00135 lflist.Process(replies);
00136
00137 for (unsigned i = 0; i < replies.size(); ++i)
00138 source.Reply(replies[i]);
00139
00140 source.Reply(_("End of news list."));
00141 }
00142
00143 return;
00144 }
00145
00146 void DoAdd(CommandSource &source, const std::vector<Anope::string> ¶ms, NewsType ntype, const char **msgs)
00147 {
00148 const Anope::string text = params.size() > 1 ? params[1] : "";
00149
00150 if (text.empty())
00151 this->OnSyntaxError(source, "ADD");
00152 else
00153 {
00154 if (Anope::ReadOnly)
00155 source.Reply(READ_ONLY_MODE);
00156
00157 NewsItem *news = new NewsItem();
00158 news->type = ntype;
00159 news->text = text;
00160 news->time = Anope::CurTime;
00161 news->who = source.GetNick();
00162
00163 this->ns->AddNewsItem(news);
00164
00165 source.Reply(msgs[MSG_ADDED]);
00166 }
00167
00168 return;
00169 }
00170
00171 void DoDel(CommandSource &source, const std::vector<Anope::string> ¶ms, NewsType ntype, const char **msgs)
00172 {
00173 const Anope::string &text = params.size() > 1 ? params[1] : "";
00174
00175 if (text.empty())
00176 this->OnSyntaxError(source, "DEL");
00177 else
00178 {
00179 std::vector<NewsItem *> &list = this->ns->GetNewsList(ntype);
00180 if (list.empty())
00181 source.Reply(msgs[MSG_LIST_NONE]);
00182 else
00183 {
00184 if (Anope::ReadOnly)
00185 source.Reply(READ_ONLY_MODE);
00186 if (!text.equals_ci("ALL"))
00187 {
00188 try
00189 {
00190 unsigned num = convertTo<unsigned>(text);
00191 if (num > 0 && num <= list.size())
00192 {
00193 this->ns->DelNewsItem(list[num - 1]);
00194 source.Reply(msgs[MSG_DELETED], num);
00195 return;
00196 }
00197 }
00198 catch (const ConvertException &) { }
00199
00200 source.Reply(msgs[MSG_DEL_NOT_FOUND], text.c_str());
00201 }
00202 else
00203 {
00204 for (unsigned i = list.size(); i > 0; --i)
00205 this->ns->DelNewsItem(list[i - 1]);
00206 source.Reply(msgs[MSG_DELETED_ALL]);
00207 }
00208 }
00209 }
00210
00211 return;
00212 }
00213
00214 void DoNews(CommandSource &source, const std::vector<Anope::string> ¶ms, NewsType ntype)
00215 {
00216 if (!this->ns)
00217 return;
00218
00219 const Anope::string &cmd = params[0];
00220
00221 const char **msgs = findmsgs(ntype);
00222 if (!msgs)
00223 throw CoreException("news: Invalid type to do_news()");
00224
00225 if (cmd.equals_ci("LIST"))
00226 return this->DoList(source, ntype, msgs);
00227 else if (cmd.equals_ci("ADD"))
00228 return this->DoAdd(source, params, ntype, msgs);
00229 else if (cmd.equals_ci("DEL"))
00230 return this->DoDel(source, params, ntype, msgs);
00231 else
00232 this->OnSyntaxError(source, "");
00233
00234 return;
00235 }
00236 public:
00237 NewsBase(Module *creator, const Anope::string &newstype) : Command(creator, newstype, 1, 2), ns("NewsService", "news")
00238 {
00239 this->SetSyntax(_("ADD \037text\037"));
00240 this->SetSyntax(_("DEL {\037num\037 | ALL}"));
00241 this->SetSyntax(_("LIST"));
00242 }
00243
00244 virtual ~NewsBase()
00245 {
00246 }
00247
00248 virtual void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) = 0;
00249
00250 virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) = 0;
00251 };
00252
00253 class CommandOSLogonNews : public NewsBase
00254 {
00255 public:
00256 CommandOSLogonNews(Module *creator) : NewsBase(creator, "operserv/logonnews")
00257 {
00258 this->SetDesc(_("Define messages to be shown to users at logon"));
00259 }
00260
00261 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00262 {
00263 return this->DoNews(source, params, NEWS_LOGON);
00264 }
00265
00266 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00267 {
00268 this->SendSyntax(source);
00269 source.Reply(" ");
00270 source.Reply(_("Edits or displays the list of logon news messages. When a\n"
00271 "user connects to the network, these messages will be sent\n"
00272 "to them. (However, no more than \002%d\002 messages will be\n"
00273 "sent in order to avoid flooding the user. If there are\n"
00274 "more news messages, only the most recent will be sent.)\n"
00275 "NewsCount can be configured in services.conf.\n"
00276 " \n"
00277 "LOGONNEWS may only be used by Services Operators."), Config->NewsCount);
00278 return true;
00279 }
00280 };
00281
00282 class CommandOSOperNews : public NewsBase
00283 {
00284 public:
00285 CommandOSOperNews(Module *creator) : NewsBase(creator, "operserv/opernews")
00286 {
00287 this->SetDesc(_("Define messages to be shown to users who oper"));
00288 }
00289
00290 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00291 {
00292 return this->DoNews(source, params, NEWS_OPER);
00293 }
00294
00295 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00296 {
00297 this->SendSyntax(source);
00298 source.Reply(" ");
00299 source.Reply(_("Edits or displays the list of oper news messages. When a\n"
00300 "user opers up (with the /OPER command), these messages will\n"
00301 "be sent to them. (However, no more than \002%d\002 messages will\n"
00302 "be sent in order to avoid flooding the user. If there are\n"
00303 "more news messages, only the most recent will be sent.)\n"
00304 "NewsCount can be configured in services.conf.\n"
00305 " \n"
00306 "OPERNEWS may only be used by Services Operators."), Config->NewsCount);
00307 return true;
00308 }
00309 };
00310
00311 class CommandOSRandomNews : public NewsBase
00312 {
00313 public:
00314 CommandOSRandomNews(Module *creator) : NewsBase(creator, "operserv/randomnews")
00315 {
00316 this->SetDesc(_("Define messages to be randomly shown to users at logon"));
00317 }
00318
00319 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00320 {
00321 return this->DoNews(source, params, NEWS_RANDOM);
00322 }
00323
00324 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00325 {
00326 this->SendSyntax(source);
00327 source.Reply(" ");
00328 source.Reply(_("Edits or displays the list of random news messages. When a\n"
00329 "user connects to the network, one (and only one) of the\n"
00330 "random news will be randomly chosen and sent to them.\n"
00331 " \n"
00332 "RANDOMNEWS may only be used by Services Operators."));
00333 return true;
00334 }
00335 };
00336
00337 class OSNews : public Module
00338 {
00339 Serialize::Type newsitem_type;
00340 MyNewsService newsservice;
00341
00342 CommandOSLogonNews commandoslogonnews;
00343 CommandOSOperNews commandosopernews;
00344 CommandOSRandomNews commandosrandomnews;
00345
00346 void DisplayNews(User *u, NewsType Type)
00347 {
00348 std::vector<NewsItem *> &newsList = this->newsservice.GetNewsList(Type);
00349 if (newsList.empty())
00350 return;
00351
00352 Anope::string msg;
00353 if (Type == NEWS_LOGON)
00354 msg = _("[\002Logon News\002 - %s] %s");
00355 else if (Type == NEWS_OPER)
00356 msg = _("[\002Oper News\002 - %s] %s");
00357 else if (Type == NEWS_RANDOM)
00358 msg = _("[\002Random News\002 - %s] %s");
00359
00360 static unsigned cur_rand_news = 0;
00361 unsigned displayed = 0;
00362 for (unsigned i = 0, end = newsList.size(); i < end; ++i)
00363 {
00364 if (Type == NEWS_RANDOM && i != cur_rand_news)
00365 continue;
00366
00367 const BotInfo *gl = Global;
00368 if (!gl && !BotListByNick->empty())
00369 gl = BotListByNick->begin()->second;
00370 const BotInfo *os = OperServ;
00371 if (!os)
00372 os = gl;
00373 if (gl)
00374 u->SendMessage(Type != NEWS_OPER ? gl : os, msg.c_str(), Anope::strftime(newsList[i]->time).c_str(), newsList[i]->text.c_str());
00375
00376 ++displayed;
00377
00378 if (Type == NEWS_RANDOM)
00379 {
00380 ++cur_rand_news;
00381 break;
00382 }
00383 else if (displayed >= Config->NewsCount)
00384 break;
00385 }
00386
00387
00388 if (Type == NEWS_RANDOM && cur_rand_news >= newsList.size())
00389 cur_rand_news = 0;
00390 }
00391
00392 public:
00393 OSNews(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
00394 newsitem_type("NewsItem", NewsItem::Unserialize), newsservice(this), commandoslogonnews(this), commandosopernews(this), commandosrandomnews(this)
00395 {
00396 this->SetAuthor("Anope");
00397
00398 Implementation i[] = { I_OnUserModeSet, I_OnUserConnect };
00399 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00400 }
00401
00402 void OnUserModeSet(User *u, const Anope::string &mname) anope_override
00403 {
00404 if (mname == "OPER")
00405 DisplayNews(u, NEWS_OPER);
00406 }
00407
00408 void OnUserConnect(User *user, bool &) anope_override
00409 {
00410 if (user->Quitting() || !user->server->IsSynced())
00411 return;
00412
00413 DisplayNews(user, NEWS_LOGON);
00414 DisplayNews(user, NEWS_RANDOM);
00415 }
00416 };
00417
00418 MODULE_INIT(OSNews)