Чат проверен на opera, explorer, firefox, chrome
для установки делаем изменения

В базу:

Код:
CREATE TABLE `nschat` (
`id` mediumint(8) unsigned NOT NULL auto_increment,
`name` varchar(25) NOT NULL default '',
`user_id` mediumint(8) NOT NULL,
`privto` varchar(25) NOT NULL default '',
`s_time` int(11) NOT NULL default '0',
`msg` varchar(1000) NOT NULL,
`lev` tinyint unsigned not null default '0',
KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER table bb_users add column nschat_on tinyint(1) not null default 1;

---open
templates/default/css/main.css
---find
.row6, .row6 td { background: #E9E9E6; }
---add after
.row9, .row9 td { background: #FFB0B0; }

---open language/lang_russian/lang_main.php
---add at end
$lang['NSCHAT'] = 'Мини-чат';
$lang['NSchat_refresh'] = 'Обновить';
$lang['NSchat_smiles'] = 'Смайлы';
$lang['NSchat_text'] = 'Сообщение';
$lang['NSchat_delmsg'] = 'удалить это сообщение';
$lang['NSchat_prof'] = 'профиль пользователя';
$lang['NSchat_priv'] = 'ник в чат или приватное сообщение';

---open
includes/ucp/usercp_viewprofile.php
---find
'GENDER' => ($bb_cfg['gender'] && $profiledata['user_gender']) ? $lang['GENDER_SELECT'][$profiledata['user_gender']] : '',
---add after
'L_NSCHAT' => $lang['NSCHAT'],
'NSCHAT' => $profiledata['nschat_on'] ? $lang['YES'] : $lang['NO'],

---open
includes/ucp/usercp_register.php
---find
'user_gender' => true,
---add after
'nschat_on' => true,
---find
case 'user_gender':
---add before
case 'nschat_on':
$nschaten=isset($_POST['nschat_on']) ? 1 : 0;
if ($submit && $nschaten!=$pr_data['nschat_on'])
{
$pr_data['nschat_on']=$nschaten;
$db_data['nschat_on']=$nschaten;
}
$tp_data['NSCHAT']=$pr_data['nschat_on'] ? " checked":"";
break;

---open
templates/default/usercp_register.tpl
---find
<!-- IF $bb_cfg['pm_notify_enabled'] -->
---add before
<td>{L_NSCHAT}:</td>
<td><label><input type="checkbox" value="1" name="nschat_on"{NSCHAT}></label></td>

---open templates/default/index.tpl
---find <!-- IF LOGGED_IN -->
---add after
<!-- IF NSCHAT_ON -->
<table width="100%" cellspacing="0" border="0" align="center" cellpadding="2" class="forumline">
<tr><td align="center" nowrap="nowrap" class="catHead"><span class="cattitle">{NSCHAT}</span></td></tr>
<tr><td class="row2">
<iframe src="./nschat.php" scrolling="NO" width="100%" height="200" frameborder="0" marginheight="0" id="69427" marginwidth="0" allowtransparency="true"></iframe></td></tr>
</table><br/>
<!-- ENDIF -->

---open index.php
---find 'FORUM_LOCKED_IMG' => $images['forum_locked'],
---add after
'NSCHAT_ON' => ($userdata['nschat_on'] != 0) ? true : false,
'NSCHAT' => $lang['NSCHAT'],

Скриншот:
http://fun-torrent.org/attachments/chatscr-png.829/

В images впихиваем:
http://s2.uploads.ru/t/nkQgy.gif
http://s2.uploads.ru/t/2XUic.gif

Создаем файл nsc_check.php и в корень трекера:

nsc_check.php

<?php
header('Content-type: application/xml');
$memc = new Memcache; $memc->connect('localhost', 11211);
if (!$shn=$memc->get('nsc_num')){
echo "99999";
} else {
echo $shn;
}

Создаем файл nschat.php и в корень трекера:

nschat.php

<?php
/*------------
-- NShut mini-chat v0.1
--------------*/

$nsc_count=50; //message count in chat
$nsc_bbcode=true; //bbcode on off
$nsc_refr=9; //refresh interval in seconds

define('IN_PHPBB', true);
include('./common.php');
$user->session_start();
if ($userdata['user_id']==ANONYMOUS) { die('Anonymous not allowed'); }
$memc=new Memcache;
$memc->connect('localhost', 11211) or die ("Error: memcache not found");

function smiliesold_pass ($message)
{ static $smilies;
       if (!isset($smilies))
        {  $smilies = $GLOBALS['datastore']->get('smile_replacements');  }
        if ($smilies)
        { $message = preg_replace($smilies['orig'], $smilies['repl'], $message);
        }   return $message;
}
function smallbb($txt)
{ global $nsc_bbcode;
        $tt=preg_replace('/\[b\](.+?)\[\/b\]/i',"<b>$1</b>",$txt);
if ($nsc_bbcode){
        $tt=preg_replace('/\[s\](.+?)\[\/s\]/i',"<s>$1</s>",$tt);
        $tt=preg_replace('/\[u\](.+?)\[\/u\]/i',"<u>$1</u>",$tt);
        $tt=preg_replace('/\[i\](.+?)\[\/i\]/i',"<i>$1</i>",$tt); }
        $tt=preg_replace("/(http|https|ftp):\/\/(([A-Za-z0-9\.\-\/?=&_#\%])*)/","<a target=\"_blank\" href=\"$1://$2\">$1://$2</a>",$tt);
        return smiliesold_pass($tt);
}

function rebuild_cache($rebuild){
//--sql to cache if not defined
  global $memc,$nscn,$nsc_count,$userdata;
if (!$nscn=$memc->get('nsc_num')){$memc->set('nsc_num',1,false,43200); } else
{ if ($nscn>30000){$nscn=1;}
  if ($rebuild){$memc->set('nsc_num',$nscn+1,false,43200);}
}
$need_rebuild=$rebuild;
if (!$nsc_data=$memc->get('nsc_data')){ $need_rebuild=true;}
if ($need_rebuild){
  $ressql=DB()->sql_query("SELECT * from nschat where privto='' order by id desc limit ".$nsc_count);
   $lastid=0;
   while ($sql_row = DB()->sql_fetchrow($ressql))
    {
$sql_row['msg']=smallbb($sql_row['msg']);
$to_memcache[]=$sql_row; $lastid=$sql_row['id'];
    }
    if (isset($to_memcache)) $memc->set('nsc_data',$to_memcache,false,43200);
    DB()->sql_freeresult($ressql);
//store to cache private user names
if (($userdata['user_level']==MOD)||($userdata['user_level']==ADMIN)){
//clean sqlbase if admin or mod do somthing
DB()->sql_query("delete from nschat where id<".$lastid);
}
  $ressql=DB()->sql_query("SELECT name,privto from nschat where id>".$lastid." and privto!=''");
   while ($sql_row = DB()->sql_fetchrow($ressql))
    {
$memc->set("n_s".$sql_row['name'],1,false,43200);
$memc->set("n_s".$sql_row['privto'],1,false,43200);
    }
    DB()->sql_freeresult($ressql);
}
}

function pastrow($t,$f,$m,$i,$u,$p,$l){
global $userdata,$lang;
//dont touch datastore, color only favorite
switch($l){
case 1: $ff="<span class=\"colorAdmin\">".$f."</span>"; break;
case 2: $ff="<span class=\"colorMod\">".$f."</span>"; break;
default:
$ff=$f;
}
  if ($p == ''){ $cls='row2';$prv='';} else {$cls='row9';$prv="<b>".$p."</b>";}
  $candel="<a href=\"javascript:nsc_do('del',".$i.");\" title=\"".$lang['NSchat_delmsg']."\"><img src=\"/images/nsdel.gif\" style='float:right'></a>";
  if (($userdata['user_level']!=MOD)&&($userdata['user_level']!=ADMIN)){
  if (($userdata['user_id']!=$u)&&($userdata['username']!=$p)){$candel="";}
  }
  if (isset($userdata['user_timezone'])){ $tz=$userdata['user_timezone'];
    } else { $tz=null; }
  $tm=gmdate('H:i',$t+(3600*$tz+3600));
  $prf="<a href=\"/profile.php?mode=viewprofile&u=".$u."\" target=\"_blank\" title=\"".$lang['NSchat_prof']."\"><img src=\"/images/nsprof.gif\"></a>";
  return "<tr><td class=\"".$cls."\" width=\"100%\"><span style=\"FONT-SIZE: 8pt\">".$candel."<span style=\"letter-spacing: -1px\">[".$tm."]</span>&nbsp;".$prf."<b><a href=\"javascript:nsc_to('".$f."');\" title='".$lang['NSchat_priv']."'>".$ff."</a></b>: ".$prv.$m."</span>";
}

$action='';
if (isset($_GET['act'])){ $action=$_GET['act'];}
switch($action){
case 'show'://ajax show chat
header('Content-type: application/xml');
if (!$nscn=$memc->get('nsc_num')){ rebuild_cache(false); }
if (!$nsc_data=$memc->get('nsc_data')){ rebuild_cache(false); }
$ret="</table>";
$usn=$userdata['username'];
if (!$memc->get('n_s'.$usn)){
  if (isset($nsc_data)) foreach ($nsc_data as $nsc_row)
{
  $txt=$nsc_row['msg'];
  $from=$nsc_row['name'];
  $ret=pastrow($nsc_row['s_time'],$from,$txt,$nsc_row['id'],$nsc_row['user_id'],'',$nsc_row['lev']).$ret;
}
} else {//get from sql because have private
  $ressql=DB()->sql_query("SELECT * from nschat where privto='' OR privto='".$usn."' OR name='".$usn."' order by id desc limit ".$nsc_count);
   $nopriv=true;
   while ($sql_row = DB()->sql_fetchrow($ressql))
    {
if ($sql_row['privto'] != '') $nopriv=false;
$ret=pastrow($sql_row['s_time'],$sql_row['name'],smallbb($sql_row['msg']),$sql_row['id'],$sql_row['user_id'],$sql_row['privto'],$sql_row['lev']).$ret;
    }
    if ($nopriv) $memc->delete('n_s'.$usn);
    DB()->sql_freeresult($ressql);

}
echo "<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"forumline\">".$ret;

break;
case 'add':
header('Content-type: application/xml');
if (isset($_POST['msg'])){
$mess=htmlspecialchars(trim(stripslashes($_POST['msg'])));
$pname='';
if (preg_match('/^\[p\](.+?)\[\/p\].+/',$mess) ){
  $ptxt=preg_replace('/^\[p\](.+?)\[\/p\]/i',"",$mess);
  $pname=preg_replace('/^\[p\](.+?)\[\/p\].+/i',"$1",$mess);
  $mess=$ptxt; }
if (!empty($mess)){
    DB()->sql_query("INSERT into nschat (name,user_id,s_time,msg,privto,lev) VALUES ('".$userdata['username']."','".$userdata['user_id']."','".time()."','".$mess."','".$pname."',".$userdata['user_level'].")");
rebuild_cache(true);
}
}
break;
case 'del':
        header('Content-type: application/xml');
if ($_POST['delid']){
$sql="DELETE from nschat where id=".$_POST['delid'];
if (($userdata['user_level']!=MOD)&&($userdata['user_level']!=ADMIN)){
  $sql.=" and (user_id=".$userdata['user_id']." OR privto='".$userdata['username']."')";
}
DB()->sql_query($sql);
rebuild_cache(true);
}
        break;

default:
  if (!$nscn=$memc->get('nsc_num')){ rebuild_cache(false); }
  header('Content-type: text/html; charset=utf-8');
?>
<link rel="stylesheet" href="./templates/default/css/main.css?v=1" type="text/css">
<script type="text/javascript" src="./misc/js/jquery.pack.js"></script>
<div id="nschat_data" style="width:100%;height:178;overflow:auto;padding-bottom:3px" onmouseover="clearInterval(nsc_scroll);">Loading...</div>
<form method="post" name="post" action="javascript:nsc_do('add',0);">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="row2">
<tr>
<td class="row2" align="center" valign="middle" width="100%">
   <span style="FONT-SIZE: 8pt">
   <input type="button" class="button" accesskey="s" name="addbbcode6" value="<?php echo $lang['NSchat_smiles']; ?>" onClick="window.open('posting.php?mode=smilies', '_phpbbsmilies', 'HEIGHT=450,resizable=yes,scrollbars=yes,WIDTH=600');return false;" />&nbsp;&nbsp;
<input value="B" style="width: 25px; font-weight: bold;" type="button" class="button" onclick="nsc_bb('b');">
<input value="i" style="width: 25px; font-style: italic;" type="button" class="button" onclick="nsc_bb('i');">
<input value="u" style="width: 25px; text-decoration: underline;" type="button" class="button" onclick="nsc_bb('u');">
<input value="S" style="width: 25px; text-decoration: line-through;" type="button" class="button" onclick="nsc_bb('s');">&nbsp;&nbsp;
        <input autocomplete=off type="text" class="liteoption" name="message" value="" size="65%" id="nsctxt" onselect="operafix(this);" onclick="operafix(this);" onkeyup="operafix(this);"/>&nbsp;&nbsp;
        <input type="button" class="liteoption" value="<?php echo $lang['SUBMIT'];?>" onclick="nsc_do('add',0);" name="nschat" />
        <input type="button" class="liteoption" value="<?php echo $lang['NSchat_refresh'];?>" onclick="nsc_load();nsctxt.focus();" name="nscrfr" />
</tr>
</table>
<script type="text/javascript">
var nscdiv=document.getElementById('nschat_data');
function nsc_to(txt)
{ var prepn=""+txt+" ";
if (prepn==document.getElementById('nsctxt').value){
  document.getElementById('nsctxt').value="[p]"+txt+"[/p] ";
} else {
  document.getElementById('nsctxt').value+=""+txt+" ";}
$('#nsctxt').focus();
}
var operaendfix=0,operabegfix=0;
function operafix(txt){ operaendfix=txt.selectionEnd;
operabegfix=txt.selectionStart; }
function nsc_bb(tg)
{
var txt=document.getElementById('nsctxt');txt.focus();
if (document.selection)
{   var sel = document.selection.createRange();
  sel.text = "["+tg+"]" + sel.text + "[/"+tg+"]"; sel.select();
} else {
  var beg=txt.selectionStart;  var end=txt.selectionEnd;
  if (beg == end) {beg=operabegfix; end=operaendfix;}
  var frg="["+tg+"]"+txt.value.substring(beg,end)+"[/"+tg+"]";
  var ln=frg.length;
  txt.value=txt.value.substring(0,beg) + frg + txt.value.substring(end,txt.value.length);
if (beg==end){ txt.setSelectionRange(beg+3,beg+3); } else { txt.setSelectionRange(beg,end+7); }
}
}
var nsc_scroll,nsc_scrollc; function nsc_scr(){ nsc_scrollc++; if (nsc_scrollc>15){ clearInterval(nsc_scroll); } if (nscdiv.scrollTop != nscdiv.scrollHeight){nscdiv.scrollTop = nscdiv.scrollHeight;}}
function nsc_load()
{
$.ajax({
        url: "/nschat.php?act=show",
        beforeSend: function( xhr ) { xhr.overrideMimeType( 'text/plain; charset=utf8' ); },
           cache: false, dataType: "html",
           success: function(html){
                nscdiv.innerHTML=html;
    nscdiv.scrollTop = nscdiv.scrollHeight;
    //img and other error set scroll by time, fix this
    clearInterval(nsc_scroll);
    nsc_scrollc=0;nsc_scroll=setInterval('nsc_scr()',300);
           },
        error:function(html){ nscdiv.innerHTML='Ajax error'; }
        });
}
var nsc_id=<?php echo $nscn; ?>;
function nsc_check()
{
        $.ajax({
        url: "/nsc_check.php",
        beforeSend: function( xhr ) {
        xhr.overrideMimeType( 'text/plain; charset=utf8' ); },
        cache: false, dataType: "html",
        success: function(html){
          if (html!=nsc_id){ nsc_load(); nsc_id=html; }
        },
        error:function(html){  } });
}

function nsc_do(act,delmsg)
{
$.ajax({
        url: "/nschat.php?act="+act,
type: "POST",
data: {msg: $('#nsctxt').val(), delid: delmsg },
        beforeSend: function( xhr ) { xhr.overrideMimeType( 'text/plain; charset=utf8' ); },
           cache: false, dataType: "html",
           success: function(html){
    document.getElementById('nsctxt').value='';
    $('#nsctxt').focus();
    nsc_load(); nsc_id++;
           },
        error:function(html){ }
        });
}
$(document).ready(function(){ nsc_load();setInterval('nsc_check()',<?php echo $nsc_refr; ?>000); }); </script>
<?php }