Anope IRC Services  Version 2.0
os_news.cpp
Go to the documentation of this file.
1 /* OperServ core functions
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at info@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/os_news.h"
14 
15 /* List of messages for each news type. This simplifies message sending. */
16 
17 enum
18 {
27 };
28 
29 struct NewsMessages msgarray[] = {
30  {NEWS_LOGON, "LOGON",
31  {_("LOGONNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
32  _("Logon news items:"),
33  _("There is no logon news."),
34  _("Added new logon news item."),
35  _("Logon news item #%s not found!"),
36  _("Logon news item #%d deleted."),
37  _("No logon news items to delete!"),
38  _("All logon news items deleted.")}
39  },
40  {NEWS_OPER, "OPER",
41  {_("OPERNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
42  _("Oper news items:"),
43  _("There is no oper news."),
44  _("Added new oper news item."),
45  _("Oper news item #%s not found!"),
46  _("Oper news item #%d deleted."),
47  _("No oper news items to delete!"),
48  _("All oper news items deleted.")}
49  },
50  {NEWS_RANDOM, "RANDOM",
51  {_("RANDOMNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
52  _("Random news items:"),
53  _("There is no random news."),
54  _("Added new random news item."),
55  _("Random news item #%s not found!"),
56  _("Random news item #%d deleted."),
57  _("No random news items to delete!"),
58  _("All random news items deleted.")}
59  }
60 };
61 
63 {
65  {
66  data["type"] << this->type;
67  data["text"] << this->text;
68  data["who"] << this->who;
69  data["time"] << this->time;
70  }
71 
73  {
74  if (!news_service)
75  return NULL;
76 
77  NewsItem *ni;
78  if (obj)
80  else
81  ni = new MyNewsItem();
82 
83  unsigned int t;
84  data["type"] >> t;
85  ni->type = static_cast<NewsType>(t);
86  data["text"] >> ni->text;
87  data["who"] >> ni->who;
88  data["time"] >> ni->time;
89 
90  if (!obj)
91  news_service->AddNewsItem(ni);
92  return ni;
93  }
94 };
95 
96 class MyNewsService : public NewsService
97 {
98  std::vector<NewsItem *> newsItems[3];
99  public:
101 
103  {
104  for (unsigned i = 0; i < 3; ++i)
105  for (unsigned j = 0; j < newsItems[i].size(); ++j)
106  delete newsItems[i][j];
107  }
108 
110  {
111  return new MyNewsItem();
112  }
113 
115  {
116  this->newsItems[n->type].push_back(n);
117  }
118 
120  {
121  std::vector<NewsItem *> &list = this->GetNewsList(n->type);
122  std::vector<NewsItem *>::iterator it = std::find(list.begin(), list.end(), n);
123  if (it != list.end())
124  list.erase(it);
125  delete n;
126  }
127 
128  std::vector<NewsItem *> &GetNewsList(NewsType t)
129  {
130  return this->newsItems[t];
131  }
132 };
133 
134 #define lenof(a) (sizeof(a) / sizeof(*(a)))
135 static const char **findmsgs(NewsType type)
136 {
137  for (unsigned i = 0; i < lenof(msgarray); ++i)
138  if (msgarray[i].type == type)
139  return msgarray[i].msgs;
140  return NULL;
141 }
142 
143 class NewsBase : public Command
144 {
146 
147  protected:
148  void DoList(CommandSource &source, NewsType ntype, const char **msgs)
149  {
150  std::vector<NewsItem *> &list = this->ns->GetNewsList(ntype);
151  if (list.empty())
152  source.Reply(msgs[MSG_LIST_NONE]);
153  else
154  {
155  ListFormatter lflist(source.GetAccount());
156  lflist.AddColumn(_("Number")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Text"));
157 
158  for (unsigned i = 0, end = list.size(); i < end; ++i)
159  {
161  entry["Number"] = stringify(i + 1);
162  entry["Creator"] = list[i]->who;
163  entry["Created"] = Anope::strftime(list[i]->time, NULL, true);
164  entry["Text"] = list[i]->text;
165  lflist.AddEntry(entry);
166  }
167 
168  source.Reply(msgs[MSG_LIST_HEADER]);
169 
170  std::vector<Anope::string> replies;
171  lflist.Process(replies);
172 
173  for (unsigned i = 0; i < replies.size(); ++i)
174  source.Reply(replies[i]);
175 
176  source.Reply(_("End of news list."));
177  }
178 
179  return;
180  }
181 
182  void DoAdd(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype, const char **msgs)
183  {
184  const Anope::string text = params.size() > 1 ? params[1] : "";
185 
186  if (text.empty())
187  this->OnSyntaxError(source, "ADD");
188  else
189  {
190  if (Anope::ReadOnly)
191  source.Reply(READ_ONLY_MODE);
192 
193  NewsItem *news = new MyNewsItem();
194  news->type = ntype;
195  news->text = text;
196  news->time = Anope::CurTime;
197  news->who = source.GetNick();
198 
199  this->ns->AddNewsItem(news);
200 
201  source.Reply(msgs[MSG_ADDED]);
202  Log(LOG_ADMIN, source, this) << "to add a news item";
203  }
204 
205  return;
206  }
207 
208  void DoDel(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype, const char **msgs)
209  {
210  const Anope::string &text = params.size() > 1 ? params[1] : "";
211 
212  if (text.empty())
213  this->OnSyntaxError(source, "DEL");
214  else
215  {
216  std::vector<NewsItem *> &list = this->ns->GetNewsList(ntype);
217  if (list.empty())
218  source.Reply(msgs[MSG_LIST_NONE]);
219  else
220  {
221  if (Anope::ReadOnly)
222  source.Reply(READ_ONLY_MODE);
223  if (!text.equals_ci("ALL"))
224  {
225  try
226  {
227  unsigned num = convertTo<unsigned>(text);
228  if (num > 0 && num <= list.size())
229  {
230  this->ns->DelNewsItem(list[num - 1]);
231  source.Reply(msgs[MSG_DELETED], num);
232  Log(LOG_ADMIN, source, this) << "to delete a news item";
233  return;
234  }
235  }
236  catch (const ConvertException &) { }
237 
238  source.Reply(msgs[MSG_DEL_NOT_FOUND], text.c_str());
239  }
240  else
241  {
242  for (unsigned i = list.size(); i > 0; --i)
243  this->ns->DelNewsItem(list[i - 1]);
244  source.Reply(msgs[MSG_DELETED_ALL]);
245  Log(LOG_ADMIN, source, this) << "to delete all news items";
246  }
247  }
248  }
249 
250  return;
251  }
252 
253  void DoNews(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype)
254  {
255  if (!this->ns)
256  return;
257 
258  const Anope::string &cmd = params[0];
259 
260  const char **msgs = findmsgs(ntype);
261  if (!msgs)
262  throw CoreException("news: Invalid type to do_news()");
263 
264  if (cmd.equals_ci("LIST"))
265  return this->DoList(source, ntype, msgs);
266  else if (cmd.equals_ci("ADD"))
267  return this->DoAdd(source, params, ntype, msgs);
268  else if (cmd.equals_ci("DEL"))
269  return this->DoDel(source, params, ntype, msgs);
270  else
271  this->OnSyntaxError(source, "");
272 
273  return;
274  }
275  public:
276  NewsBase(Module *creator, const Anope::string &newstype) : Command(creator, newstype, 1, 2), ns("NewsService", "news")
277  {
278  this->SetSyntax(_("ADD \037text\037"));
279  this->SetSyntax(_("DEL {\037num\037 | ALL}"));
280  this->SetSyntax("LIST");
281  }
282 
283  virtual ~NewsBase()
284  {
285  }
286 
287  virtual void Execute(CommandSource &source, const std::vector<Anope::string> &params) = 0;
288 
289  virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) = 0;
290 };
291 
293 {
294  public:
295  CommandOSLogonNews(Module *creator) : NewsBase(creator, "operserv/logonnews")
296  {
297  this->SetDesc(_("Define messages to be shown to users at logon"));
298  }
299 
300  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
301  {
302  return this->DoNews(source, params, NEWS_LOGON);
303  }
304 
305  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
306  {
307  this->SendSyntax(source);
308  source.Reply(" ");
309  source.Reply(_("Edits or displays the list of logon news messages. When a\n"
310  "user connects to the network, these messages will be sent\n"
311  "to them. However, no more than \002%d\002 messages will be\n"
312  "sent in order to avoid flooding the user. If there are\n"
313  "more news messages, only the most recent will be sent."),
314  Config->GetModule(this->owner)->Get<unsigned>("newscount", "3"));
315  return true;
316  }
317 };
318 
320 {
321  public:
322  CommandOSOperNews(Module *creator) : NewsBase(creator, "operserv/opernews")
323  {
324  this->SetDesc(_("Define messages to be shown to users who oper"));
325  }
326 
327  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
328  {
329  return this->DoNews(source, params, NEWS_OPER);
330  }
331 
332  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
333  {
334  this->SendSyntax(source);
335  source.Reply(" ");
336  source.Reply(_("Edits or displays the list of oper news messages. When a\n"
337  "user opers up (with the /OPER command), these messages will\n"
338  "be sent to them. However, no more than \002%d\002 messages will\n"
339  "be sent in order to avoid flooding the user. If there are\n"
340  "more news messages, only the most recent will be sent."),
341  Config->GetModule(this->owner)->Get<unsigned>("newscount", "3"));
342  return true;
343  }
344 };
345 
347 {
348  public:
349  CommandOSRandomNews(Module *creator) : NewsBase(creator, "operserv/randomnews")
350  {
351  this->SetDesc(_("Define messages to be randomly shown to users at logon"));
352  }
353 
354  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
355  {
356  return this->DoNews(source, params, NEWS_RANDOM);
357  }
358 
359  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
360  {
361  this->SendSyntax(source);
362  source.Reply(" ");
363  source.Reply(_("Edits or displays the list of random news messages. When a\n"
364  "user connects to the network, one (and only one) of the\n"
365  "random news will be randomly chosen and sent to them."));
366  return true;
367  }
368 };
369 
370 static unsigned cur_rand_news = 0;
371 
372 class OSNews : public Module
373 {
376 
380 
382  unsigned news_count;
383 
384  void DisplayNews(User *u, NewsType Type)
385  {
386  std::vector<NewsItem *> &newsList = this->newsservice.GetNewsList(Type);
387  if (newsList.empty())
388  return;
389 
390  BotInfo *bi = NULL;
391  if (Type == NEWS_OPER)
392  bi = BotInfo::Find(Config->GetModule(this)->Get<const Anope::string>("oper_announcer", "OperServ"), true);
393  else
394  bi = BotInfo::Find(Config->GetModule(this)->Get<const Anope::string>("announcer", "Global"), true);
395  if (bi == NULL)
396  return;
397 
398  Anope::string msg;
399  if (Type == NEWS_LOGON)
400  msg = _("[\002Logon News\002 - %s] %s");
401  else if (Type == NEWS_OPER)
402  msg = _("[\002Oper News\002 - %s] %s");
403  else if (Type == NEWS_RANDOM)
404  msg = _("[\002Random News\002 - %s] %s");
405 
406  unsigned displayed = 0;
407  for (unsigned i = 0, end = newsList.size(); i < end; ++i)
408  {
409  if (Type == NEWS_RANDOM && i != cur_rand_news)
410  continue;
411 
412  u->SendMessage(bi, msg.c_str(), Anope::strftime(newsList[i]->time, u->Account(), true).c_str(), newsList[i]->text.c_str());
413 
414  ++displayed;
415 
416  if (Type == NEWS_RANDOM)
417  {
418  ++cur_rand_news;
419  break;
420  }
421  else if (displayed >= news_count)
422  break;
423  }
424 
425  /* Reset to head of list to get first random news value */
426  if (Type == NEWS_RANDOM && cur_rand_news >= newsList.size())
427  cur_rand_news = 0;
428  }
429 
430  public:
431  OSNews(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
432  newsservice(this), newsitem_type("NewsItem", MyNewsItem::Unserialize),
433  commandoslogonnews(this), commandosopernews(this), commandosrandomnews(this)
434  {
435  }
436 
438  {
439  oper_announcer = conf->GetModule(this)->Get<const Anope::string>("oper_announcer", "OperServ");
440  announcer = conf->GetModule(this)->Get<const Anope::string>("announcer", "Global");
441  news_count = conf->GetModule(this)->Get<unsigned>("newscount", "3");
442  }
443 
444  void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
445  {
446  if (mname == "OPER")
448  }
449 
450  void OnUserConnect(User *user, bool &) anope_override
451  {
452  if (user->Quitting() || !user->server->IsSynced())
453  return;
454 
455  DisplayNews(user, NEWS_LOGON);
456  DisplayNews(user, NEWS_RANDOM);
457  }
458 };
459 
Definition: bots.h:24
CoreExport bool ReadOnly
Definition: main.cpp:28
unsigned news_count
Definition: os_news.cpp:382
NewsType type
Definition: os_news.h:20
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: os_news.cpp:305
NewsBase(Module *creator, const Anope::string &newstype)
Definition: os_news.cpp:276
std::vector< NewsItem * > & GetNewsList(NewsType t)
Definition: os_news.cpp:128
void DisplayNews(User *u, NewsType Type)
Definition: os_news.cpp:384
bool IsSynced() const
Definition: servers.cpp:298
const char * msgs[10]
Definition: os_news.h:15
CommandOSLogonNews commandoslogonnews
Definition: os_news.cpp:377
void AddNewsItem(NewsItem *n)
Definition: os_news.cpp:114
void OnUserConnect(User *user, bool &) anope_override
Definition: os_news.cpp:450
Definition: users.h:34
NewsItem * CreateNewsItem() anope_override
Definition: os_news.cpp:109
Anope::string who
Definition: os_news.h:22
Serialize::Type newsitem_type
Definition: os_news.cpp:375
virtual ~NewsBase()
Definition: os_news.cpp:283
#define READ_ONLY_MODE
Definition: language.h:71
struct NewsMessages msgarray[]
Definition: os_news.cpp:29
void SetDesc(const Anope::string &d)
Definition: command.cpp:130
bool equals_ci(const char *_str) const
Definition: anope.h:78
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: os_news.cpp:359
CoreExport time_t CurTime
Definition: main.cpp:41
const Anope::string & GetNick() const
Definition: command.cpp:26
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: os_news.cpp:354
time_t time
Definition: os_news.h:23
std::map< Anope::string, Anope::string > ListEntry
Definition: lists.h:68
void DoNews(CommandSource &source, const std::vector< Anope::string > &params, NewsType ntype)
Definition: os_news.cpp:253
ServiceReference< NewsService > ns
Definition: os_news.cpp:145
bool Quitting() const
Definition: users.cpp:763
#define lenof(a)
Definition: os_news.cpp:134
Anope::string oper_announcer
Definition: os_news.cpp:381
Definition: Config.cs:26
void DoDel(CommandSource &source, const std::vector< Anope::string > &params, NewsType ntype, const char **msgs)
Definition: os_news.cpp:208
virtual void OnSyntaxError(CommandSource &source, const Anope::string &subcommand)
Definition: command.cpp:191
static unsigned cur_rand_news
Definition: os_news.cpp:370
CommandOSRandomNews(Module *creator)
Definition: os_news.cpp:349
void Reply(const char *message,...)
Definition: command.cpp:96
virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand)=0
void DoAdd(CommandSource &source, const std::vector< Anope::string > &params, NewsType ntype, const char **msgs)
Definition: os_news.cpp:182
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
#define MODULE_INIT(x)
Definition: modules.h:45
Server * server
Definition: users.h:77
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
OSNews(const Anope::string &modname, const Anope::string &creator)
Definition: os_news.cpp:431
void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
Definition: os_news.cpp:444
Anope::string stringify(const T &x)
Definition: anope.h:710
static ServiceReference< NewsService > news_service("NewsService","news")
void DelNewsItem(NewsItem *n)
Definition: os_news.cpp:119
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: os_news.cpp:332
CommandOSOperNews commandosopernews
Definition: os_news.cpp:378
void OnReload(Configuration::Conf *conf) anope_override
Definition: os_news.cpp:437
static Serializable * Unserialize(Serializable *obj, Serialize::Data &data)
Definition: os_news.cpp:72
MyNewsService(Module *m)
Definition: os_news.cpp:100
NickCore * Account() const
Definition: users.cpp:422
NewsType
Definition: os_news.h:4
T anope_dynamic_static_cast(O ptr)
Definition: anope.h:774
Anope::string announcer
Definition: os_news.cpp:381
void SendSyntax(CommandSource &)
Definition: command.cpp:145
MyNewsService newsservice
Definition: os_news.cpp:374
NickCore * GetAccount()
Definition: command.cpp:36
CommandOSLogonNews(Module *creator)
Definition: os_news.cpp:295
const char * c_str() const
Definition: anope.h:117
Definition: logger.h:53
void DoList(CommandSource &source, NewsType ntype, const char **msgs)
Definition: os_news.cpp:148
Anope::string text
Definition: os_news.h:21
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: os_news.cpp:300
static const char ** findmsgs(NewsType type)
Definition: os_news.cpp:135
void Serialize(Serialize::Data &data) const anope_override
Definition: os_news.cpp:64
CoreExport Anope::string strftime(time_t t, const NickCore *nc=NULL, bool short_output=false)
Definition: misc.cpp:356
CommandOSOperNews(Module *creator)
Definition: os_news.cpp:322
void SendMessage(BotInfo *source, const char *fmt,...)
Definition: users.cpp:320
#define _(x)
Definition: services.h:50
CommandOSRandomNews commandosrandomnews
Definition: os_news.cpp:379
static BotInfo * Find(const Anope::string &nick, bool nick_only=false)
Definition: bots.cpp:249
ListFormatter & AddColumn(const Anope::string &name)
Definition: misc.cpp:128
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: os_news.cpp:327
virtual void Execute(CommandSource &source, const std::vector< Anope::string > &params)=0
Type(const Anope::string &n, unserialize_func f, Module *owner=NULL)
std::vector< NewsItem * > newsItems[3]
Definition: os_news.cpp:98