#ifdef RCS
static char rcsid[]="$Id: news.c,v 1.1.1.1 2000/11/13 02:42:45 holsta Exp $";
#endif
/******************************************************************************
* Internetting Cooperating Programmers
* ----------------------------------------------------------------------------
*
* ____ PROJECT
* | _ \ __ _ _ __ ___ ___ _ __
* | | | |/ _` | '_ \ / __/ _ \ '__|
* | |_| | (_| | | | | (_| __/ |
* |____/ \__,_|_| |_|\___\___|_| the IRC bot
*
* All files in this archive are subject to the GNU General Public License.
*
* $Source: /cvsroot/dancer/dancer/src/news.c,v $
* $Revision: 1.1.1.1 $
* $Date: 2000/11/13 02:42:45 $
* $Author: holsta $
* $State: Exp $
* $Locker: $
*
* ---------------------------------------------------------------------------
*****************************************************************************/
/******************************************************************************
Suggested way of a NEWS system for Dancer
=========================================
The NEWS system is a kind of mass-tell that shows information to persons with
a certain level or higher when they join. Each NEWS item is only showed once
to each user (if not otherwise explicitly requested). The NEWS items will
automatically be removed after a certain number of days in the list.
The system will of course prevent too many NEWS items to get displayed if a
user joins and X news items are to get displayed where X is larger than a
certain limit. The system will then fall back to say that "there are X news
items to get displayed, use NEWSREAD to read them".
Suggested commands:
NEWSADD [ -e expires] [-U] [-p match] <level> <text>
Adds a new NEWS item to the list.
-U marks the news item as... urgent. ;)
<match> is a wildcard match the receiver must match to receive the news.
<expires> is the number of days the news is valid. Default will be set in
the .config file controlling the bot (internal default 90 days).
<level> is the least level required for a user to get this info displayed.
Level 0 (or 'all') makes all registered users get the info.
When invoked, this command will output the 'newsid' which can be used to
remove it or explicitly read it with NEWSREAD.
NEWSDEL <id>
Removes the specified NEWS item from the list.
NEWSLIST [number of items]
Lists the X latest NEWS items from the list. Detailed with author, date, id
and level info.
NEWSREAD [id] (actually a TELLME alias)
With no argument, this presents the oldest unread NEWS entry. With argument
it presents the item associated with the specified id.
-a reads all items at once
-c clear all, make all items marked as read without showing them
User-data:
AUTONEWS
Controls how a user gets the news presented on join. Three different options:
'NEVER' - I don't want to see any news at all (at join)
'INFO' - I just want to be told there is news (and number of unread items)
'<number>' - I want to see the first (oldest) news item on joining, when the
news level is higher than this number (default is 0). If none
match but there are unmatched ones, the INFO way will be used
Info stored per NEWS item:
<id> - 32 bit number (unique number)
<date> - 32 bit number (time of entry, seconds since 1970)
<level> - 32 bit number (lowest level required to get this)
<author> - string (who wrote this)
<flags> - string (URGENT?)
<expire> - 32 bit number (days to keep the entry)
<match> - string (userhost match pattern)
<text> - text to end of line
NEW ONES
<count> - read counter
<last> - time of last reading
******************************************************************************/
#include "dancer.h"
#include "trio.h"
#include "strio.h"
#include "list.h"
#include "user.h"
#include "news.h"
extern char newsfile[];
extern long levels[];
extern time_t now;
extern itemident *current;
int numNews = 0;
itemnews *newsHead = NULL;
static long newsId = 0; /* Use the function to access */
/* --- NewsAddItem ------------------------------------------------ */
void FreeNews(void *v)
{
itemnews *n;
snapshot;
n = (itemnews *)v;
if (n) {
if (n->author)
StrFree(n->author);
if (n->news)
StrFree(n->news);
if (n->match)
StrFree(n->match);
numNews--;
}
}
/* Returns the newly added newsitem on success or NULL on error */
itemnews *NewsAddItem(char *author,
long level,
long id,
time_t time,
long flags,
long expire,
char *match,
char *news)
{
itemnews *n;
snapshot;
n = NewEntry(itemnews);
if (n) {
InsertLast(newsHead, n);
n->level = level;
n->id = id;
n->time = time;
n->flags = flags;
n->expire = expire;
n->author = StrDuplicate(author);
n->match = StrDuplicate(match ? match : "*");
n->news = StrDuplicate(news);
numNews++;
}
return n;
}
void NewsSave(void)
{
char tempfile[MIDBUFFER];
bool ok = TRUE;
itemnews *n, *next;
FILE *f;
snapshot;
if (NIL == newsfile[0])
return;
/* Remove expired news */
for (n = First(newsHead); n; n = next) {
next = Next(n);
if ((now - n->time) > (n->expire * SECINDAY))
DeleteEntry(newsHead, n, FreeNews);
}
StrFormatMax(tempfile, sizeof(tempfile), "%s~", newsfile);
f = fopen(tempfile, "w");
if (f) {
if (0 > fprintf(f, "News seqno: %d\n", newsId)) {
ok = FALSE;
}
else {
for (n = First(newsHead); n; n = Next(n)) {
if (0 > fprintf(f, "%s %d %d %d %d %d %s :%s\n",
n->author, n->level, n->id, n->time,
n->flags, n->expire, n->match, n->news)) {
ok = FALSE;
break;
}
}
}
fclose(f);
if (ok)
rename(tempfile, newsfile);
}
}
void NewsInit(void)
{
char buf[MAXLINE];
char news[BIGBUFFER];
char author[BIGBUFFER];
char match[MIDBUFFER];
long flags;
long expire;
long id;
long level;
time_t time;
FILE *f;
snapshot;
newsHead = NewList(itemnews);
if (newsfile[0] && (f = fopen(newsfile, "r"))) {
if (fgets(buf, MAXLINE, f)) {
StrScan(buf, "News seqno: %d", &newsId);
while (fgets(buf, MAXLINE, f)) {
news[0] = (char)0;
if (8 <= StrScan(buf, "%"BIGBUFFERTXT"s %d %d %d %d %d "
"%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
author, &level, &id, &time, &flags, &expire,
match, news)) {
NewsAddItem(author, level, id, time, flags, expire, match, news);
}
}
}
fclose(f);
}
}
void NewsCleanup(void)
{
snapshot;
NewsSave();
DeleteList(newsHead, FreeNews);
}
/* returns TRUE if something went wrong */
bool NewsDelete(long id, long flags)
{
itemnews *n;
snapshot;
for (n = First(newsHead); n; n = Next(n)) {
if (n->id == id) {
DeleteEntry(newsHead, n, FreeNews);
return FALSE;
}
}
return TRUE;
}
/***********************************************************************
*
* NewsRead()
* ==========
* Returns the number of unread news including the pointed one.
*
* The input ID is the high water mark to use.
*
* The newsp pointer points to a 'itemnews' pointer that will point out
* the current news item to read.
*
***********************************************************************/
long NewsRead(long id, itemnews **newsp)
{
long unread = 0;
itemnews *n;
snapshot;
if (newsp)
*newsp = NULL; /* default to nothing */
for (n = First(newsHead); n; n = Next(n)) {
if ((n->id > id) && (current->level >= n->level)) {
if (Match(current->host, n->match)) {
if (newsp && !*newsp)
*newsp = n;
unread++;
}
}
}
return unread;
}
long NewsID(void)
{
return ++newsId;
}
/***********************************************************************
*
* NewsReport()
*
* Report news for a user at join.
*
**********************************************************************/
void NewsReport(itemident *ident)
{
itemnews *n;
snapshot;
if (NewsRead(ident->user->newsid, &n)) {
Sendf(ident->nick, GetText(msg_news_report),
n->news,
n->author,
TimeAgo(n->time));
ident->user->newsid = n->id;
}
}
static long CounterNews(void)
{
long count = 0;
itemnews *n;
snapshot;
for (n = First(newsHead); n; n = Next(n)) {
if ((current->level < n->level) ||
((current->level <= LEVELCHANOP) &&
!Match(current->host, n->match)))
continue;
count++;
}
return count;
}
void NewsList(char *from, long lines)
{
int max;
long count = 0;
itemnews *n;
snapshot;
max = CounterNews();
for (n = First(newsHead); n; n = Next(n)) {
if ((n->level > current->level) ||
((current->level <= LEVELCHANOP) && !Match(n->match, current->host)))
continue;
if (max <= (count + lines)) {
Sendf(from, GetText(msg_news_list),
(n->id > current->user->newsid) ? GetText(msg_unread) : "",
n->id, n->level, n->match, n->author, TimeAgo(n->time));
}
count++;
}
if (0 == count)
Sendf(from, GetText(msg_no_news));
}
syntax highlighted by Code2HTML, v. 0.9.1