1# $Id: centericq.pl,v 1.0.0 2002/10/19 13:15:49 Garion Exp $
2use strict;
3use vars qw($VERSION %IRSSI);
4$VERSION = "1.0.0";
5%IRSSI = (
6    authors     => "Joost \"Garion\" Vunderink",
7    contact     => "joost\@carnique.nl",
8    name        => "centericq",
9    description => "Staturbar item which indicates how many new messages you have in your centericq",
10    sbitems     => "centericq",
11    license     => "Public Domain",
12    url         => "http://irssi.org, http://scripts.irssi.org",
13);
14
15# centericq new messages statusbar item
16# for irssi 0.8.4 by Timo Sirainen
17#
18# This statusbar item checks whether you have unread messages in
19# ~/.centericq/, and if so, displays a status in your statusbar.
20# Example status: [ICQ: JamesOff-1,Linn-3,Paul-4]
21#
22# Use:
23# /script load centericq
24# /statusbar <name> add centericq
25#
26# Known bugs:
27# - It only works for ICQ and MSN in centericq.
28# - The refreshing is not optimal. You'll need to swap windows to make
29#   the statusbar item disappear if you've read the messages.
30# - You have to reload the script if you add new people in centericq.
31# - Works only with centericq in ~/.centericq/
32#
33# TODO:
34# - Use only the first N letters of the nickname instead of the full
35#   nickname.
36
37use Irssi;
38use Irssi::TextUI;
39
40my $icqdir = $ENV{'HOME'} . "/.centericq";
41my ($last_refresh_time, $refresh_tag);
42my $statusbar_item;
43
44# The following vars are all hashes with key the name of the dir in
45# ~/.centericq/ of that person
46
47my %lastreads;    # Timestamp of the last read message, per nick
48my %numunreads;   # Number of unread messages, per nick
49my %historyts;    # Timestamp of the history file of each nick
50my %lastreadts;   # Timestamp of the lastread file of each nick
51my %friendnicks;  # The nicknames of the friends
52
53
54#######################################################################
55# This is the function that will be called each N seconds, where
56# N is given by the centericq_refresh_time setting.
57
58sub refresh_centericq {
59  check_new_friends();
60
61  my @friends = keys(%lastreads);
62  my ($friend, $changed) = ("", 0);
63  foreach $friend (@friends) {
64    if (history_changed($friend) || lastread_changed($friend)) {
65      $changed = 1;
66      update_status($friend);
67    }
68  }
69
70  if ($changed) {
71    update_statusbar_item();
72  }
73
74  # Adding new timeout to make sure that this function will be called
75  # again
76  if ($refresh_tag) {
77    Irssi::timeout_remove($refresh_tag)
78  }
79  my $time = Irssi::settings_get_int('centericq_refresh_time');
80  $refresh_tag = Irssi::timeout_add($time*1000, 'refresh_centericq', undef);
81}
82
83
84#######################################################################
85# Checks if any new friends have been added. Not yet functional.
86
87sub check_new_friends {
88  #Irssi::print("Checking if there are any new friends...");
89}
90
91#######################################################################
92# Checks if the last modified date/time of the lastread file has changed.
93# A -lot- more efficient than reading and processing the whole file :)
94
95sub lastread_changed {
96  my ($friend) = @_;
97
98  my $lr = get_lastread($friend);
99  if ($lr != $lastreads{$friend}) {
100    #Irssi::print("Lastread of $friendnick{$friend} changed from $lastreads{$friend} to $lr.");
101    $lastreads{$friend} = $lr;
102    return 1;
103  }
104
105  return 0;
106}
107
108#######################################################################
109# Checks if the last modified date/time of the history file has changed.
110# A -lot- more efficient than reading and processing the whole file :)
111
112sub history_changed {
113  my ($friend) = @_;
114  my $ts = get_historyts($friend);
115  if ($ts != $historyts{$friend}) {
116    #Irssi::print("History ts of $friendnick{$friend} changed from $historyts{$friend} to $ts.");
117    $historyts{$friend} = $ts;
118    return 1;
119  }
120
121  return 0;
122}
123
124#######################################################################
125# Reads the last read message and determines the number of unread
126# messages of $friend.
127
128sub update_status {
129  my ($friend) = @_;
130  $lastreads{$friend}   = get_lastread($friend);
131  $numunreads{$friend}  = get_numunreads($friend);
132}
133
134#######################################################################
135# Gets the number of unread messages of all nicks and puts them together
136# in a nice statusbar string.
137# It then requests a statusbar item redraw.
138
139sub update_statusbar_item {
140  #Irssi::print("Updating statusbaritem...");
141  $statusbar_item = "";
142
143  my @keys = keys(%lastreads);
144  my ($key, $status);
145
146  foreach $key(@keys) {
147    if ($numunreads{$key} > 0) {
148      #Irssi::print("$friendnick{$key} has $numunreads{$key} unreads.");
149      $status .= $friendnicks{$key} . "-" . $numunreads{$key} . ",";
150    }
151  }
152  $status =~ s/,$//;
153  if (length($status) > 0) {
154    $statusbar_item = "ICQ: " . $status;
155    Irssi::statusbar_items_redraw('centericq');
156  }
157}
158
159
160#######################################################################
161# This is the function called by irssi to obtain the statusbar item
162# for centericq.
163
164sub centericq {
165  my ($item, $get_size_only) = @_;
166
167  if (length($statusbar_item) == 0) {
168    # no icq - don't print the [ICQ] at all
169    if ($get_size_only) {
170      $item->{min_size} = $item->{max_size} = 0;
171    }
172  } else {
173    $item->default_handler($get_size_only, undef, $statusbar_item, 1);
174  }
175}
176
177#######################################################################
178# Initialization of the hashes with the useful data.
179
180sub init {
181  if (!opendir(ICQDIR, $icqdir)) {
182    Irssi::print("There is no directory $icqdir, which is needed for this script.");
183    return 0;
184  }
185
186  my ($icqfriends, $msnfriends) = (0, 0);
187  while (my $filename = readdir(ICQDIR)) {
188    # ICQ friends
189    if ($filename =~ /^[0-9]+$/ && $filename !~ /^0$/) {
190      $icqfriends++;
191      init_friend($filename);
192    }
193    # MSN friends
194    if ($filename =~ /^m.+/ && $filename !~ /^modelist$/ ) {
195      $msnfriends++;
196      init_friend($filename);
197    }
198  }
199  Irssi::print("Watching $icqfriends ICQ friends and $msnfriends MSN friends.");
200
201  closedir(ICQDIR);
202  return 1;
203}
204
205#######################################################################
206# Initialises all data of $friend
207
208sub init_friend {
209  my ($friend) = @_;
210
211  $lastreads{$friend}   = get_lastread($friend);
212  $numunreads{$friend}  = get_numunreads($friend);
213  #$filesizes{$friend}   = get_filesize($friend);
214  $friendnicks{$friend}  = get_nickname($friend);
215  $historyts{$friend}   = get_historyts($friend);
216  #Irssi::print("Initilialized $friendnick{$friend}.");
217}
218
219#######################################################################
220# Returns the last read message of $friend
221
222sub get_lastread {
223  my ($friend) = @_;
224  my $lastreadfile = $icqdir . "/" . $friend . "/lastread";
225
226  open(F, "<", $lastreadfile) || return 0; #die("Could not open $lastreadfile.");;
227  my $lastrd = <F>;
228  close(F);
229  chop($lastrd);
230  #Irssi::print("Found lastread $lastrd of $friend from $lastreadfile.");
231
232  return $lastrd;
233}
234
235#######################################################################
236# Returns the number of unread messages for $friend
237
238sub get_numunreads {
239  my ($friend) = @_;
240  my $lr = $lastreads{$friend};
241  # Unknown last read message - return 0.
242  if ($lr == 0) {
243    return 0;
244  }
245
246  my $msgfile = $icqdir . "/" . $friend . "/history";
247  open(F, "<", $msgfile) || return 0; #die("Could not open $msgfile.");
248  my @lines = <F>;
249  chop(@lines);
250  close(F);
251
252  my $numlines = @lines;
253
254  # read all lines up to the lastread message
255  my $line;
256  my $bla = 0;
257  do {
258    $line = shift(@lines);
259    $bla++;
260  } while ($line ne $lr);
261
262  # now count the number of times that "MSG" is found on a line below
263  # a line with "IN"
264  my $count = 0;
265  my $incoming = 0;
266  my $verify = 0;
267  my $bli = 0;
268
269  for (@lines) {
270    $bli++;
271    # Sometimes 2 messages get in at the same time. Remove this so-called
272    # new message if it has the same time as the last read message.
273    if ($verify == 1) {
274      if ($_ =~ /$lr/) {
275        $count--;
276      }
277      $verify = 0;
278    }
279    # A line with "IN" has been found; check if the next line is "MSG".
280    if ($incoming == 1) {
281      if ($_ =~ /^MSG/) {
282        $count++;
283	$verify = 1;
284      }
285      $incoming = 0;
286    }
287    # Check for "IN".
288    if ($_ =~ /^IN/) {
289      $incoming = 1;
290    }
291  }
292
293  return $count;
294}
295
296#######################################################################
297# Returns the nickname of a friend. This is taken from the 46th line
298# of the info file. Let's hope that centericq does not change its
299# config file format.
300
301sub get_nickname {
302  my ($friend) = @_;
303
304  my $infofile = $icqdir . "/" . $friend . "/info";
305  open(F, "<", $infofile) || return $friend; #die("Could not open $msgfile.");
306  my @lines = <F>;
307  chop(@lines);
308  close(F);
309
310  return $lines[45];
311}
312
313#######################################################################
314# Returns the timestamp of the history file of $friend.
315
316sub get_historyts {
317  my ($friend) = @_;
318  my $histfile = $icqdir . "/" . $friend .  "/history";
319  my @stat = stat($histfile);
320  return $stat[9];
321}
322
323#######################################################################
324# Adding stuff to irssi
325
326Irssi::settings_add_int('misc', 'centericq_refresh_time', 120);
327#Irssi::settings_add_bool('misc', 'centericq_debug', 0);
328Irssi::statusbar_item_register('centericq', '{sb $0-}', 'centericq');
329
330#######################################################################
331# Startup functions
332
333if (init() == 0) {
334  Irssi::print("You need centericq for this script.");
335  return 0;
336}
337update_statusbar_item();
338refresh_centericq();
339
340Irssi::print("Centericq statusbar item loaded.");
341
342#######################################################################
343