вторник, 14 октября 2008 г.

Linux. Чат-бот Рубик в виде LUA скрипта

Ура! Наконец-то! Я это сделал.
Мой чат-бот для DBhub'a был переписан с Perl'a на язык скриптов LUA. Отдельное спасибо за это форуму сайта www.lua.ru

За основу был взят чат-бот Кубик, которого я сначала хотел "проапгрейдить". В итоге это практически вышло, но влекло много минусов, вроде того, что база хранилась бы лишь в ОЗУ, что совершенно не интересно.

В итоге я реализовал его как и в Perl'e - может немного и коряво, но он работает, а это главное =)
Остальное можно поправить в процессе.

Итак, если у вас есть поддержка LUA-скриптов (например стоит PtokaX-хаб, хотя я так понял они где только не используются) - вы можете поставить себе "Рубика".
Копируете код в файл, сохраняете как "Rubik.lua" и вуаля! =)


***** Код *****

--[[
Rubik bot 0.5 для LUA
основан на коде полностью измененного Кубика
модифицировал TrenAr

История версий бота:
0.1 - Первый релиз! =)
0.2 - Теперь он "А?"кает и введена защита от пустых фраз
0.3 - Рубик перестал принимать фразы в базу, если они записаны в 2 и более строки. Иначе база слетала
0.4 - Из анализируемой фразы было выкинуто имя самого Рубика, что приводило к заеданию бота на какую-то фразу
0.5 - Повышен искуственный интеллект. Фразы с большим совпадением имеют приоритет при выборе ответа.

Версия для LUA 5.0.2 / 5.1.1 by NRJ

Сконвертировано под Lua 5.1.3 неизвестно кем :) Конвертер, отзовись ;)

Таблица trigs взята из одноименного скрипта перевода romiros'a
Поддержка русских букв взята из NOYELL script от NoNick'a

]]--

BotName = "Рубик" -- имя бота
BotDesc = "Обучаемый бот" -- описание бота
BotEmail = "" -- email бота

------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------

-- Таблица ников-исключений,чьи фразы бот не будет комментировоть (примеры ниже)
-- Фигня, она не работает, но нужна для работоспособности бота
TableName = {
["Админ"] = 1,
}

------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------

Rus={["А"]="а",["Б"]="б",["В"]="в",["Г"]="г",["Д"]="д",["Е"]="е",["Ё"]="ё",["Ж"]="ж",["З"]="з",["И"]="и",["Й"]="й",["К"]="к",["Л"]="л",["М"]="м",["Н"]="н",["О"]="о",["П"]="п",["Р"]="р",["С"]="с",["Т"]="т",["У"]="у",["Ф"]="ф",["Х"]="х",["Ц"]="ц",["Ч"]="ч",["Ш"]="ш",["Щ"]="щ",["Ъ"]="ъ",["Ы"]="ы",["Ь"]="ь",["Э"]="э",["Ю"]="ю",["Я"]="я"}

function OnStartup()
if (_VERSION == "Lua 5.1.1") or (_VERSION == "Lua 5.1") then
TableMaxSize = table.maxn
elseif (_VERSION == "Lua 5.0.2") then
TableMaxSize = table.getn
end

Core.RegBot(BotName,BotDesc,BotEmail,true)
end

function ChatArrival(curUser,data)
Core.GetUserAllData(curUser)

if TableName[curUser.sNick] ~=1 then if string.sub(data, 1, 1) then
data=string.sub(data,1,string.len(data)-1)
s,e,cmd,RestOfText = string.find( data, "%b<>%s+(%S+)%s+(.*)" )
if RestOfText == nil then
RestOfText = ""
s,e,cmd = string.find( data, "%b<>%s+(%S+)" )
end
end


s,e,mess = string.find(data, "^%b<>%s(.*)$")

-- Моё - проверка на наличие имени Рубика.
-- А также вероятность написать в чат, даже если ему ниче не пишут
-- It's work!
--
-- rndsay = math.random(1,35)
-- if (string.find( mess, "Рубик")) or (rndsay == 3) then
-- Сам пока писать не будет. Дабы не было глюков

if (string.find( mess, "Рубик")) then

-- Убираем имя Рубика
rubname = string.find( mess, "Рубик")
mymess=string.sub(mess , rubname + 6, string.len(mess))
-- Если имя написано с двоеточием
if (string.find( mess, "Рубик:")) then mymess=string.sub(mess , rubname + 7, string.len(mess)) end

-- Читаем базу на наличие вхождений фраз из неё в тексте пришедшего сообщения от юзера
-- Считаем кол-во вхождений
kol = 0
maxlen = 0
maxlensum = 0
maxlenansw = ""

local handle = io.open("RubikBase.txt","r")
if handle then
str1 = handle:read(65)
while str1 do
str2 = handle:read(129)
-- Отрезаем у строк "хвосты"
hvost1=string.find( str1, "%-%-%-")
str1=string.sub(str1 , 1, hvost1-1)
hvost2=string.find( str2, "%-%-%-")
str2=string.sub(str2 , 1, hvost2-1)
-- Если нашли str1 - плюсуем кол-во, ну и условия по макс.длине
if (string.find( mymess, str1)) then
kol = kol+1
if (string.len(str1) >= maxlen) then
maxlen = string.len(str1)
maxlenansw = str2
end
maxlensum = maxlensum + string.len(str1)
end
str1 = handle:read(65)
end
handle:close()
end

-- Второй проход по файлу. Выбираем рандомно ответ и задаём ответ по-умолчанию
answer = "А?"
-- Пробегаем 2 раз, только если kol>0
if (kol > 0) then
local handle = io.open("RubikBase.txt","r")
if handle then
str1 = handle:read(65)
while str1 do
str2 = handle:read(129)
-- Отрезаем у строк "хвосты"
hvost1=string.find( str1, "%-%-%-")
str1=string.sub(str1 , 1, hvost1-1)
hvost2=string.find( str2, "%-%-%-")
str2=string.sub(str2 , 1, hvost2-1)
-- Итак, выбор ответа
if (string.find( mymess, str1)) then
rnd = math.random(1,maxlensum)
if (rnd < answer =" str2" str1 =" handle:read(65)" answer ="="" answer =" maxlenansw" st =" string.find(" line1="string.sub(mymess" line2="string.sub(mymess"> 2 ) and ( string.len(line1) <> 2 ) and ( string.len(line2) < line1 =" line1.." line2 =" line2.." handle =" io.open("> Отделяем в фразе часть ДО " - " и ПОСЛЕ - строки line и line2 соответственно
-- Т.е. если нашли " - "
end


-- Ответ бота =)
Core.SendToAll("<"..curUser.sNick.."> "..cmd.." "..RestOfText)
Core.SendToAll("<"..BotName.."> "..curUser.sNick.." ".. answer )
return true

-- end к проверке на имя Бота в сообщении (которая It's work)
end

-- Дальше оригинальный код
-- Нда... очень оригинальный =)

end
end

***** Конец кода *****



* ВНИМАНИЕ *
Код, будучи выложен в общий доступ, заинтересовал одного замечательного товарища, с ником SCALOlaz. Ему понравилась идея и он стал её дорабатывать (после версии 0.5). И прикрутил к боту очень много всяких полезных дополнительных фич. Код, правда, выкладывать ему лень, но выложу я, ибо такое добро пропадать не должно. Поэтмоу настоятельно рекомендую использовать его код, а не мой.



***** Начало кода *****

--[[
Rubik bot для LUA
основан на коде полностью измененного Кубика
модифицировал TrenAr
после версии 0.5 модифицировал SCALOlaz

История версий бота:
0.1 - Первый релиз! =)
0.2 - Теперь он "А?"кает и введена защита от пустых фраз
0.3 - Рубик перестал принимать фразы в базу, если они записаны в 2 и более строки. Иначе база слетала
0.4 - Из анализируемой фразы было выкинуто имя самого Рубика, что приводило к заеданию бота на какую-то фразу
0.5 - Повышен искуственный интеллект. Фразы с большим совпадением имеют приоритет при выборе ответа.

Версия для LUA 5.0.2 / 5.1.1 by NRJ

Сконвертировано под Lua 5.1.3 неизвестно кем :) Конвертер, отзовись ;)

Таблица trigs взята из одноименного скрипта перевода romiros'a
Поддержка русских букв взята из NOYELL script от NoNick'a

0.6 [16/11/2008]
- Теперь бот выводит имя собеседника не постоянно
- База сохраняется четко в папке RUBIK скриптов
- Вместо "А?" всегда разные ответы на пустые фразы ( массив и переменная DefAnswers (Cat) )

0.9 [10/1/2009]
- Теперь бот настраивается операторами и админом (Имя, Описание, Автофлуд, Частота)
- Выводит фразы в чат самостоятельно
- ТеПеРь НеЗаВиСиМо от РеГиСтРа, отвечает сохраненными фразами
- Добавлен просмотр Базы (выдает в личку)

+- Возможность поиска по слову
+- Возможность удаления первого вхождения по слову

]]--

RubikBotName = "Зина" -- имя бота
-- RubikBotName = SetMan.GetString(21)
RubikBotDesc = "..." -- описание бота
RubikBotEmail = "" -- email бота
RubikBotKey = true -- Бот с ключом или без
RubikPatch = Core.GetPtokaXPath().."scripts/RUBIK/RubikBase.txt" -- added for highest API2

BotAutoAnswer = true -- Включить автоговорилку
BotStupid = 79 -- Генерация автоговорилки (не менее 2)
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
-- Таблица ответов по умолчанию, например на пустую фразу
DefAnswersCnt = 15 -- Всего ответов
DefAnswers = {
"Чиво?",
"К чему это все?..",
"Тебе это надо?",
"Ну и?",
"Непонимаю...",
"Пафтари",
"Эй! Убери грабли!",
"А?",
"Ась ?",
"Харош жмакать мой ник!",
":спряталсо:",
"Чо хател?",
"Хто тут?!!",
"Игнор",
"Слыш, выбери кого-нить другого и спрашивай"
}


-- Таблица ников-исключений,чьи фразы бот не будет комментировоть (примеры ниже)
-- Фигня, она не работает, но нужна для работоспособности бота
--TableName = {
-- ["Админ"] = 1,
--}
-- БОЛЬШЕ НЕ НУЖНА

--Rus={["А"]="а",["Б"]="б",["В"]="в",["Г"]="г",["Д"]="д",["Е"]="е",["Ё"]="ё",["Ж"]="ж",["З"]="з",["И"]="и",["Й"]="й",["К"]="к",["Л"]="л",["М"]="м",["Н"]="н",["О"]="о",["П"]="п",["Р"]="р",["С"]="с",["Т"]="т",["У"]="у",["Ф"]="ф",["Х"]="х",["Ц"]="ц",["Ч"]="ч",["Ш"]="ш",["Щ"]="щ",["Ъ"]="ъ",["Ы"]="ы",["Ь"]="ь",["Э"]="э",["Ю"]="ю",["Я"]="я"}
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------

function OnStartup()
-- if (_VERSION == "Lua 5.1.1") or (_VERSION == "Lua 5.1") then
-- TableMaxSize = table.maxn
-- elseif (_VERSION == "Lua 5.0.2") then
-- TableMaxSize = table.getn
-- end

Core.RegBot(RubikBotName,RubikBotDesc,RubikBotEmail,RubikBotKey)
Core.SendToAll("<"..SetMan.GetString(21).."> --=============[ Скрипт "..RubikBotName.." был перезапущен: "..os.date("%d/%m/%y в %H:%M:%S").."! ]=============--")
end

function OpDisconnected(user) end
function UserDisconnected(user) end

function UserConnected(user)
Core.GetUserAllData(user)
RubikUserRightclicker(user,data)
end

function OpConnected(user)
Core.GetUserAllData(user)
RubikOpRightclicker(user,data)
end

function RubikUserRightclicker(user,data)
-- Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\Информация по ботам$<%[mynick]> !botshelp||")
end

function RubikOpRightclicker(user,data)

Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\"..RubikBotName.."\\Включить автофлуд$<%[mynick]> !rubickgoflood||")
Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\"..RubikBotName.."\\Выключить автофлуд бота$<%[mynick]> !rubickstopflood||")
Core.SendToNick(user.sNick,"$UserCommand 0 3 Боты\\"..RubikBotName.."\\|")
Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\"..RubikBotName.."\\Сводная информация$<%[mynick]> !rubickinfo||")
Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\"..RubikBotName.."\\Посмотреть список фраз$<%[mynick]> !rubicklistbase||")

-- Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\"..RubikBotName.."\\Удалить фразу$<%[mynick]> !rubdel %[line:Укажите номер строки]||")

if user.iProfile==0 or user.iProfile==1 then
-- if user.iProfile==0 then --тупо скроем менюшку от операторов и юзверей
Core.SendToNick(user.sNick,"$UserCommand 0 3 Боты\\"..RubikBotName.."\\|")
Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\"..RubikBotName.."\\Изменить Имя$<%[mynick]> !rubickname %[line:Введите Новый Ник]||")
Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\"..RubikBotName.."\\Изменить Описание$<%[mynick]> !rubickdesc %[line:Введите Новое Описание]||")
end

if user.iProfile==0 then
Core.SendToNick(user.sNick,"$UserCommand 1 3 Боты\\"..RubikBotName.."\\Частота флуда$<%[mynick]> !rubickrankingflood %[line:Укажите частоту флуда !ЧИСЛО (3-80)!]||")
end
end

function ChatArrival(curUser,data)
Core.GetUserAllData(curUser)
data=string.sub(data,1,string.len(data)-1)
local s,e,cmd=string.find(data,"%b<>%s+(%S+)")
cmd=string.sub(cmd,2)

if cmd=="rubicklistbase" and (curUser.iProfile==0 or curUser.iProfile==1) then

RubickBaseList = "Список фраз Базы:\r\n"
local RubickBaseLenght = 0
-- local handle = io.open("scripts/RUBIK/RubikBase.txt","r") --RubikPatch
local handle = io.open(RubikPatch,"r") --RubikPatch
-- local count = 0
if handle then
str1 = handle:read(65)
while str1 do
str2 = handle:read(129)
-- Отрезаем у строк "хвосты"
hvost1=string.find( str1, "%-%-%-")
str1=string.sub(str1 , 1, hvost1-1)
hvost2=string.find( str2, "%-%-%-")
str2=string.sub(str2 , 1, hvost2-1)
RubickBaseList=RubickBaseList.."\t"..RubickBaseLenght..":\t"..str1.." - "..str2.."\r\n"
RubickBaseLenght = RubickBaseLenght +1
str1 = handle:read(65)
end
handle:close()
end
Core.SendPmToNick(curUser.sNick,RubikBotName,RubickBaseList)
Core.SendPmToNick(curUser.sNick,RubikBotName,"Всего в Базе: "..RubickBaseLenght.." записей")
return true

--[[
elseif cmd=="rubdel" and (curUser.iProfile==0 or curUser.iProfile==1) then
local _,_,delnum=string.find(data,"(%d+)")
local delnumber = tonumber(delnum)

local handle = io.open(RubikPatch,"r")
local count = 0
local massive = ""
if handle then
str1 = handle:read(65)
while str1 do
str2 = handle:read(129)
if count ~= delnumber then
massive = massive..str1..str2
end
-- Отрезаем у строк "хвосты"
hvost1=string.find( str1, "%-%-%-")
str1=string.sub(str1 , 1, hvost1-1)
hvost2=string.find( str2, "%-%-%-")
str2=string.sub(str2 , 1, hvost2-1)
if count == delnumber then
Core.SendToNick(curUser.sNick,"<"..RubikBotName.."> Будет удалена фраза №"..count..": "..str1.." - "..str2.."")
end
count = count + 1
str1 = handle:read(65)
end
handle:close()
end
Core.SendPmToNick(curUser.sNick,RubikBotName,massive)
local handle = io.open("scripts/RUBIK/RubikBase2.txt","w+")
handle:write(massive)
handle:close()
return true
]]--

elseif cmd=="rubickgoflood" and (curUser.iProfile==0 or curUser.iProfile==1) then
BotAutoAnswer = true
Core.SendToNick(curUser.sNick,"*** Вы включили автофлуд бота. Теперь он будет говорить даже когда не просят.")
Core.SendToOps("<"..SetMan.GetString(21).."> <"..curUser.sNick.."> включил автофлуд бота <"..RubikBotName..">")

Core.SendToAll("<"..RubikBotName.."> *** Теперь я, бот '"..RubikBotName.."', буду общаться с вами даже если меня не просят!")

return true
elseif cmd=="rubickstopflood" and (curUser.iProfile==0 or curUser.iProfile==1) then
BotAutoAnswer = false
Core.SendToNick(curUser.sNick,"*** Вы отключили автофлуд бота. Он будет только отвечать на вопросы.")
Core.SendToOps("<"..SetMan.GetString(21).."> <"..curUser.sNick.."> выключил автофлуд бота <"..RubikBotName..">")

Core.SendToAll("<"..RubikBotName.."> *** Все, я молчу. Пока не спросите - ничего не скажу")



return true

elseif cmd=="rubickname" and (curUser.iProfile==0 or curUser.iProfile==1) then
local _,_,RubikNewName=string.find(data, "^%b<>%s(.*)$")
if RubikNewName ~= nil then
Core.UnregBot(RubikBotName)
Core.SendToNick(curUser.sNick,"*** Вы сменили Ник бота на '"..RubikNewName.."'")
Core.SendToOps("<"..SetMan.GetString(21).."> <"..curUser.sNick.."> сменил Ник бота <"..RubikBotName..">")

Core.SendToAll("<"..RubikBotName.."> *** Теперь меня зовут '"..RubikNewName.."'... Странно, в паспорте никаких отметок. ")
RubikBotName=RubikNewName
Core.RegBot(RubikBotName,RubikBotDesc,RubikBotEmail,RubikBotKey)
end
if RubikNewName == nil then
Core.SendToNick(curUser.sNick,"*** Ошибка! Вы не указали ник!")
end
return true

elseif cmd=="rubickdesc" and (curUser.iProfile==0 or curUser.iProfile==1) then
-- local _,_,RubikNewDesc=string.find(data,"%b<>%s+%S+%s+(%S+)")
local _,_,RubikNewDesc=string.find(data, "^%b<>%s(.*)$")
RubikNewDesc=string.sub(RubikNewDesc , 13, string.len(RubikNewDesc)) --13 - длина команды

if RubikNewDesc ~= nil then
Core.UnregBot(RubikBotName)
Core.SendToNick(curUser.sNick,"*** Вы сменили Описание бота на '"..RubikNewDesc.."'")
Core.SendToOps("<"..SetMan.GetString(21).."> <"..curUser.sNick.."> сменил описание бота <"..RubikBotName..">")

Core.SendToAll("<"..RubikBotName.."> *** Мое описание '"..RubikNewDesc.."'... Не поверите, я тоже в шоке от такого хамства!")
RubikBotDesc=RubikNewDesc
Core.RegBot(RubikBotName,RubikNewDesc,RubikBotEmail,RubikBotKey)
end
if RubikNewDesc == nil then
Core.SendToNick(curUser.sNick,"*** Ошибка! Вы не указали ник!")
end
return true

elseif cmd=="rubickrankingflood" and curUser.iProfile==0 then
local _,_,RubikRank=string.find(data,"(%d+)")
RubikRanking=tonumber(RubikRank)
-- Тут сука надо заставить его различать текст введен или число! Иначе сука крэшится

if (RubikRank ~= nil) then
if ((RubikRanking > 2) and (RubikRanking < 81)) then
Core.SendToNick(curUser.sNick,"*** Вы сменили относительную частоту флуда бота на '"..RubikRanking.."'")
Core.SendToOps("<"..SetMan.GetString(21).."> <"..curUser.sNick.."> сменил частоту флуда бота <"..RubikBotName.."> на '"..RubikRanking.."'")
BotStupid=RubikRanking
else
Core.SendToNick(curUser.sNick,"*** Ошибка! Неверный интервал! Укажите ЧИСЛО от 3 до 50-ти!")
end
else
Core.SendToNick(curUser.sNick,"*** Ошибка! Указан неверный параметр или параметр пропущен!")
end
return true

elseif cmd=="rubickinfo" then
RubikInfo = "<"..RubikBotName.."> Сводная информация о скрипте: \r\n"
RubikInfo=RubikInfo.."\t=========================================\r\n"
RubikInfo = RubikInfo.."\t- Имя бота: '"..RubikBotName.."'\r\n\t- Описание: '"..RubikBotDesc.."'\r\n"
if BotAutoAnswer == true then
RubikInfo=RubikInfo.."\t- Автофлуд: Включен\r\n"
else RubikInfo=RubikInfo.."\t- Автофлуд: Выключен\r\n"
end
if curUser.iProfile==0 then
RubikInfo=RubikInfo.."\t- Частота флуда: "..BotStupid.."\r\n\r\n"
RubikInfo=RubikInfo.."* Внимание! Частота указывается в относительной величине! В процессе опроса введённой строки рандомно выбирается число в интервале от 1 до Этого числа! При совпадении в районе +1 бот автоматически выдает фразу.\r\n"
-- RubikInfo=RubikInfo.."* Внимание! На данный момент в скрипте нет проверки на число или слово!!! Вводите ТОЛЬКО ЧИСЛАМИ, иначе скрипт упадет\r\n"
RubikInfo=RubikInfo.."\t=========================================\r\n"
Core.SendToNick(curUser.sNick,RubikInfo)
end
return true
end


-- if TableName[curUser.sNick] ~=1 then
if string.sub(data, 1, 1) then
-- data=string.sub(data,1,string.len(data)-1)
s,e,cmd,RestOfText = string.find( data, "%b<>%s+(%S+)%s+(.*)" )
if RestOfText == nil then
RestOfText = ""
s,e,cmd = string.find( data, "%b<>%s+(%S+)" )
end
-- end





s,e,mess = string.find(data, "^%b<>%s(.*)$")

-- Моё - проверка на наличие имени Рубика.
-- А также вероятность написать в чат, даже если ему ниче не пишут
-- It's work!


-- С этого момента он сам отвечает в чат
BotQuest = 0
if (string.find( mess, RubikBotName)) then BotQuest = 1 end

if (BotQuest == 0) and (BotAutoAnswer == true) then
rndsay = math.random(1,BotStupid)
if (rndsay > (BotStupid-1)) then
mess = RubikBotName..":"..mess
end
end
-- до этого момента


-- if (string.find( mess, "Рубик")) or (rndsay > (BotStupid-1)) then
-- Сам пока писать не будет. Дабы не было глюков


if (string.find( mess, RubikBotName)) then
mess = string.gsub(mess, "%%", "+" )
-- Убираем имя Рубика
rubname = string.find( mess, RubikBotName.."")
mymess=string.sub(mess , rubname + string.len(RubikBotName), string.len(mess))
-- Если имя написано с двоеточием
if (string.find( mess, RubikBotName..":")) then mymess=string.sub(mess , rubname + (string.len(RubikBotName)+1), string.len(mess)) end

-- Читаем базу на наличие вхождений фраз из неё в тексте пришедшего сообщения от юзера
-- Считаем кол-во вхождений
kol = 0
maxlen = 0
maxlensum = 0
maxlenansw = ""

-- local handle = io.open("scripts/RUBIK/RubikBase.txt","r")
local handle = io.open(RubikPatch,"r")

if handle then
str1 = handle:read(65)
while str1 do
str2 = handle:read(129)
-- Отрезаем у строк "хвосты"
hvost1=string.find( str1, "%-%-%-")
str1=string.sub(str1 , 1, hvost1-1)
hvost2=string.find( str2, "%-%-%-")
str2=string.sub(str2 , 1, hvost2-1)

--mymess - Сабж от юзверя
--str1 - Сабж в базе
mymess22=LowString(mymess)
str12=LowString(str1)

-- Если нашли str1 - плюсуем кол-во, ну и условия по макс.длине
if (string.find( mymess22, str12)) then
kol = kol+1
-- Core.SendToAll("<"..RubikBotName.."> Заявлено: " ..mymess22.." Найдено: ".. str1.. "." )

if (string.len(str1) >= maxlen) then
maxlen = string.len(str1)
maxlenansw = str2
end
maxlensum = maxlensum + string.len(str1)
end
str1 = handle:read(65)
end
handle:close()
end

-- Второй проход по файлу. Выбираем рандомно ответ и задаём ответ по-умолчанию
answreg = DefAnswers[math.random(1,DefAnswersCnt)]
answer = answreg
-- Пробегаем 2 раз, только если kol>0
if (kol > 0) then
-- local handle = io.open("scripts/RUBIK/RubikBase.txt","r")
local handle = io.open(RubikPatch,"r")

if handle then
str1 = handle:read(65)
while str1 do
str2 = handle:read(129)
-- Отрезаем у строк "хвосты"
hvost1=string.find( str1, "%-%-%-")
str1=string.sub(str1 , 1, hvost1-1)
hvost2=string.find( str2, "%-%-%-")
str2=string.sub(str2 , 1, hvost2-1)
-- Итак, выбор ответа

--mymess - Сабж от юзверя
--str1 - Сабж в базе
mymess22=LowString(mymess)
str12=LowString(str1)

if (string.find( mymess22, str12)) then
rnd = math.random(1,maxlensum)
if (rnd < string.len(str1)) then answer = str2 end
end
str1 = handle:read(65)
end
handle:close()
end
-- Если ни один ответ не выпал - берем тот, что самый длинный
if (answer == answreg) then answer = maxlenansw end
end

-----------
-- Тут пополнение базы. Если найдена фраза типа "Рубик: Молоко - Это не рыба", нужно добавить в файл с базой 2 строки: "Молоко" и "Это не рыба". Тогда Рубик в след.раз, увидив слово Молоко, скажет "Это не рыба".
-----------

-- Блок записи в файл.

-- Отделяем в фразе часть ДО " - " и ПОСЛЕ - строки line и line2 соответственно
if (string.find( mymess, " %- ")) then
st = string.find( mymess, " %- ")

line1=string.sub(mymess , 1, st-1)

line2=string.sub(mymess , st+3, string.len(mymess))

-- Если сообщения нормальной длины и в них нет символов \n и \r (перенос строки и возврат каретки), то фраза принимается
if ( string.len(line1) > 2 ) and ( string.len(line1) < 60) then
if ( string.len(line2) > 2 ) and ( string.len(line2) < 120 ) then
if not (( string.find( line1, "\n") ) or ( string.find( line1, "\r") )) then
if not (( string.find( line2, "\n") ) or ( string.find( line2, "\r") )) then

-- Пишем строки в файл.
-- Предварительно раздуем длину
while (string.len(line1)<64) do line1 = line1.."-" end
while (string.len(line2)<128) do line2 = line2.."-" end
--local handle = io.open("scripts/RUBIK/RubikBase.txt","a+")
local handle = io.open(RubikPatch,"a+")

handle:write(line1.."\n")
handle:write(line2.."\n")
handle:close()

end
end
end
end

-- End of -> Отделяем в фразе часть ДО " - " и ПОСЛЕ - строки line и line2 соответственно
-- Т.е. если нашли " - "
end


-- Ответ бота =)
Core.SendToAll("<"..curUser.sNick.."> "..cmd.." "..RestOfText)

rndanswnick = math.random(1,2)
if (rndanswnick == 2) then
Core.SendToAll("<"..RubikBotName.."> "..curUser.sNick..": "..answer )
else
Core.SendToAll("<"..RubikBotName.."> ".. answer )
end
return true

-- end к проверке на имя Бота в сообщении (которая It's work)
end

-- Дальше оригинальный код
-- Нда... очень оригинальный =)

end
end

function LowString(sData)
sData = sData:gsub("(.)", function ( s )
local iByte = s:byte()
if iByte >= 192 and iByte <= 223 then
iByte = iByte + 32
elseif iByte == 168 or iByte == 184 then --приведем Ё и ё к букве е
-- iByte = 184
iByte = 229
end
return string.char(iByte)
end)
return sData:lower()
end

***** Конец кода *****


Если вам помогла эта статья - оставьте комментарий! Они доступны даже не зарегистрированным пользователям.

За помощь в вопросе спасибо XNut.

понедельник, 13 октября 2008 г.

Linux. Редактор vi в 2 словах и 4 командах

Эта заметка посвящена она текстовому редактору "vi". Тому самому который можно найти практически в любом дистрибутиве (а может и во всех?). Тому самому, который всегда есть под рукой. И тому самому, которым не все любят пользоваться =)

Для того чтобы в нём разобраться - в нём надо работать самому. Все нюансы использования здесь не описать и описывать бессмысленно - это придет с опытом. Главная задача - начать использовать редактор.

Поэтому здесь я опишу 4 наверное самых главных команды для начинающего "вьюзера" =)

Главное, что надо отметить - переход в режим команд (а не ввода текста) в "vi" осуществляется кнопкой Esc.

1) Открыть редактор, или открыть файл в редакторе файл filename (пишется в консоли)
vi
vi filename

2) Записать в текущий файл, или в файл filename (не забудьте нажать Esc)
:w
:w filename

3) Выход (не даст выйти, если внесли изменения в файл)
:q

4) Выход с отменой всех изменений
:qa!

Если вам помогла эта статья - оставьте комментарий! Они доступны даже не зарегистрированным пользователям.

среда, 8 октября 2008 г.

Linux. DBhub, пишем чат-бота

Решил я у себя поднять DirectConnect Hub (DC-Hub то бишь).
Среди 3 дистрибутивов (Verli, PtokaX, DBhub) поставить смог только один - DBhub (http://www.dbhub.org/). Хаб в общем-то простой как в установке, так и настройке. По возможностям тоже достаточно прост и не обладает такой мощной поддержкой всякими скриптами как 2 его "соперника". Ну, по крайней мере на официальном сайте это представлено скупо, всего порядка 6 скриптов (http://www.dbhub.org/olddbhub/scripts/).
Но, хаб поддерживает Perl-скрипты, а это уже очень даже хорошо (главное не забыть при установке включить их поддержку).

Но прежде чем говорить про бота - рассмотрим базовые настройки по установке и настройке хаба. У меня помнится возникли вопросы, поэтому может вам будет проще.

Итак, взять свежий пакет можно на официальном сайте (http://www.dbhub.org/). Там представлены как исходники для компиляции, так и пакеты для распространенных систем (Debian'a, RPM'ы и прочее).
В принципе проблем с установкой как того, так и другого нет. С пакетом вообще проблем нету, если не ошибаюсь - нажали, и он встал. Другое дело что он там поставит.

Руками это можно проконтролировать, если компилировать самим. В общем-то и компилировать с особыми настройками смысла нет, можно оставить по-умолчанию. Единственное - надо добавить поддержку Perl-скриптов (если они вам нужны). Для этого надо иметь установленные пакеты Perl и perl-devel, если не ошибаюсь. А при конфигурировании указать параметр --enable-perl. Т.е. выполнить строку:
./configure --enable-perl
После чего вы узнаете, будет ли у вас хаб с поддержкой (если все нормально с Perl) или без поддержки Perl-скриптов. Наконец пишите make, make install и хаб должен встать.

Встает он каждому юзеру в папку я так понял. А может только тому, кто хаб запустит, точно не знаю. Путь такой:
домашняя_папка_юзера/.dbhub

Я запускаю под рутом, соответственно папка "/root/.dbhub".
Все настройки в этой папке и фактически собраны в одном конфигурационном файле. Описания каждой настройки подробно можно узнать из википедии DBhub'a (ссылка на сайте).
Все скрипты лежат в одноименной папке. Для их выполнения достаточно кинуть их туда и перезапустить сам хаб или только скрипты.

Хаб запускается командой "dbhub" из консоли.
Убить его можно командой "killall dbhub".
Если же нужно только перезапустить скрипты, то достаточно лишь написать от имени владельца хаба (или администратора) команду "!reloadscripts" в окне чата.

Владелец хаба и пароль к его учетной записи определяются при первом запуске, хотя потом и то и то можно изменить через конфигурационные файлы.
Для русификации надо заменить файл "lang" на русский и выполнить команду "!reloadlang" (если не ошибаюсь - подробнее читайте в описании к самому русификатору).

В общем решил я написать чат-бота. Простого такого бота, который отвечает тому, кто к нему обращается - фразой из своей базы. Ботов таких много, у меня идея возникла когда я увидел "Кубика" для хаба PtokaX (отсюда имя Rubik, как мой ответ PtokaX-боту). Бот неплох, но имеет недостатки: лезет в разговор когда не просят и его фразы быстро приедаются и надоедают.

При разработке были учтены эти 2 момента, в итоге бот отвечает только если к нему обращаются, и умеет обучаться новым фразам прямо во время разговора. Например, если написать "Rubik: Москва - Столица России!", то в след.раз встретив слово Москва бот ответит "Столица России!" =)

В общем довольно просто, но интересно при достаточно большой базе (которая быстро накапливается).
Короче говоря код бота приведен ниже. Сохраните его в файл "Rubik.pl" в кодировке CP1251 и положите в папку "/root/.dbhub/scripts" (или смотря откуда у вас запускается хаб). Если поддержка скриптов при компиляции была включена (./configure --enable-perl)



#!/usr/bin/perl

# Rubik-bot 0.3

# Моя первая Perl-программа. В этом языке практически не разбираюсь.
# Копипастил из кодов и инета. Так что извините за корявость написания кода =)
# Дополнять, копировать и т.д. никому не воспрещается.
# Просто не забывайте указывать меня в авторах ^_^
#
# My first Perl-program. My English is bad, but i hope you unerstand me =)
# Code is free-for-all. You can do with it what you want.
# But don't forget write my nick in comments ^_^
#
# TrenAr
#

### Rubik settings ###
my $bot = "Rubik"; #name of your bot
my $uhomedir = `echo ~`;
chop $uhomedir;
my $path = "$uhomedir/.dbhub/scripts/";

# Если в чате новое сообщение
# If a new message in a chat
sub data_arrival() {
my ($user, $data) = @_;

# Ищем - есть ли в чате упоминание ника бота
# Search Bot name in chat
$find = index ($data,$bot);
$findadd = index ($data,"$bot:");

# Если нашли упоминание его ника
# If find his name
if ($find > (-1)) {

# Берем то что написано после его ника, и обработка ":"
# Take the message after bot nick, without ":" if this need
$msg = substr($data,$find + length($bot)+1,length($data) - $find - length($bot));
if ($findadd > (-1)) {$msg = substr($data,$find + length($bot)+2,length($data)- $find - length($bot));}

# Ищем вероятные "определения", т.е " - "
# Find " - "
$st = " - ";
$target = index ($msg,$st);

# Здесь 2 как "защита от "пустых строк"
# Когда находим - разбиваем предложение на 2 части
# 1 it's defence
# When find $st - devine string to two string
if ($target > (2)) {
$part1 = substr($msg,0,$target);
$part2 = substr($msg,$target+length($st),length($msg) - $target - length($st) -1);

# Добавляем строки в файл
# Add strings in file
open MSSG, ">>$path/Rubik-database";
print MSSG "$part1.-.$part2";
for ($index = 0; $index <= 128 - length($part1) - length($part2) - 3; $index++)
{print MSSG "*";}
print MSSG "\n";
close MSSG;
odch::data_to_all("<$bot> Ясно |");

} else {

# Проверяем на наличие слов в фразе msg - в нашей базе
# Если есть совпадения - пишем 2 часть фразы.
# Если совпадений на одной слово несколько - пишем первое, а лучше рандомно.
#
# Look at our database and message. If it have equal phrases - $msg = $part1
# (msg - message to our bot; part1 one of words in base)
# then write in chat first phrase which true.
# Or Random - it will be more interesting.

$otv = "";
$kol = 0;
open MSSG, "<$path/Rubik-database";
while () {
read MSSG, $str, 128;

$search1 = index ($str,".-.");
if ($search1 > (-1)) {
$otvet1 = substr($str,0,$search1);
$search2 = index ($msg,$otvet1);
if ($search2 > (-1)) {
$kol++;
}
}
}
close MSSG;

open MSSG, "<$path/Rubik-database";
while () {
read MSSG, $str, 128;

$search1 = index ($str,".-.");
if ($search1 > (-1)) {
$otvet1 = substr($str,0,$search1);
$search2 = index ($msg,$otvet1);
if ($search2 > (-1)) {
$otvet2 = substr($str,$search1 + 3,length($str) - $search1 - 3);
$search3 = index ($otvet2,"**");
$otvet3 = substr($otvet2,0,$search3);
if ($otv eq "") {$otv = "$otvet3";}
else {
$tmp = int(rand($kol));
if ($tmp == 1) {$otv = "$otvet3";}
}
}

}

#while End
}
close MSSG;
odch::data_to_all("<$bot> $user $otv |");
}

# "If for Rubik" End
}
# DataArrival_End
}

sub main() {
odch::register_script_name($bot);
}

Если вам помогла эта статья - оставьте комментарий! Они доступны даже не зарегистрированным пользователям.

пятница, 3 октября 2008 г.

Linux. Музыка в сети, виртуальные диски и прочая магия mount

Звуки музыки

Бывает так, что хочется послушать музыку. Согласитесь, было такое с вами? =)
Но бывает и так, что музыка лежит не на вашей машине. Вот например у меня за неимением места она складируется на другом компьютере, откуда я её и слушаю.

И слушал я её в Windows и знать не знал что процесс сей сложнее, чем он кажется на первый взгляд. И поставил я Линукс, и отказался он музыку играть.
Amarok сказал что-то вроде не могу проиграть музыку, что-то там сеть ла-ла... ну в общем не помню. Суть в том, что не хочет он это делать.

Если у вас под рукой есть smb4k - тогда все очень просто. В программе вы легко все сделаете с помощью нескольких нажатий мышью. Но если программы под рукой нет - читайте дальше.

В консоли решение проблемы тоже простое. Надо примонтировать сетевую папку как обычный каталог, и тогда Линукс будет думать что играет музыку с диска и никаких проблем не возникнет. Точнее думать так будет Амарок, Линукс то как раз будет в курсе всех наших тайных манипуляций =)
Осталось выяснить, как эту папку примонтировать.

На самом деле все просто. Для этого вам надо:
а) знать имя удаленной машины (через ip-адрес тоже можно, но надо пробовать как это сделать)
б) иметь на ней учетную запись под которой можно зайти по сети
в) ВАЖНО! Имя и пароль к учетной записи должны быть записаны буквами латинского алфавита. По крайней мере у меня с русскими ничего не вышло, как я не пытался
г) наконец, надо иметь у себя пустую папку, в которую вы будете монтировать

Ну и, конечно, установленные пакеты клиента Samba. Что-то вроде libsmbclient (в Мандриве 08), точно по памяти не скажу.

После чего достаточно выполнить команду:

mount -t smbfs -o username=vasya,password=pupkin //NETWORKPC/Music /mnt/Music

Команда mount запускается с параметром smbfs, который указывает, что монтируется сетевая папка.
В качестве опций задаются имя "vasya" и пароль "pupkin".
Потом указывается то ОТКУДА монтируем, где NETWORKPC - имя компьютера в сети на котором лежит папка Music.
Наконец в конце пишем КУДА монтируем - в пустую папку Music в каталоге mnt.

Наконец, если вы монтируете папку в которой лежат файлы и папки с русскими буквами - нужно правильно выбрать кодировку. Для этого добавьте к опциям параметры "iocharset=utf8,codepage=cp866". Итого строка будет выглядеть так:

mount -t smbfs -o username=vasya,password=pupkin,iocharset=utf8,codepage=cp866 //NETWORKPC/Music /mnt/Music

Вот и всё - теперь вся музыка (или что вы там примонтировали?) как будто и не в сети, а у нас на диске.

Кустарный Virtual CD

А бывают еще ситуации, когда надо бы вставить ISO-образ с диском в виртуальный привод. Ну, не знаю как насчет привода, наверное тоже можно, а вот как смонтировать образ в папку - я могу рассказать.
Ситуация не сложнее чем с музыкой, пишем mount, указываем параметры, ОТКУДА, КУДА и получаем результат.
Типичная команда выглядит так:

mount -t iso9660 /home/diman/virtual/Quake3.iso /mnt/virual_cd -o loop

iso9660 - указывает что это ISO-образ
Потом пишем ГДЕ он лежит
Потом указываем КУДА его смонтировать
Наконец в конце добавляем опцию "-o loop"

Теперь мы можем установить Q3 из папки /mnt/virual_cd на нашу систему =)

Верните мне обратно!

Ну а чтобы отмонтировать, используется команда umount. В простейшем случае достаточно указать лишь то КУДА вы примонтировали и система все сделает сама. Например:

umount /mnt/virual_cd

P.S.
Надо заметить, что в процессе могут возникнуть проблемы, например с теми же кодировками. Тогда надо будет экспериментировать с параметрами, которые можно найти в "man mount"

Если вам помогла эта статья - оставьте комментарий! Они доступны даже не зарегистрированным пользователям.

За помощь в вопросе спасибо GrayCat.

Linux. Работаем с NTFS или Куда делись мои жесткие диски

Проблема довольно распространенная. Вы устанавливаете дистрибутив, загружаете, а он не видит ваши NTFS-диски. Делаете то же самое в другом дистрибутиве - там все отлично.
Как вариант - можно остаться работать в том дистрибутиве, где они видны. Но гораздо правильнее будет попытаться подключить их вручную, чем менять дистрибутив.

На каждом дистрибутиве насколько я заметил есть свои заморочки. Заключаются они в параметрах, которые надо указывать. Но более менее суть одна и подойдет к любому дистрибутиву. К тому же некоторые вещи можно либо прочесть в справке, либо дополнительно спросить на форумах.

NTFS на чтение

Для начала просто откроем нужные разделы на чтение.
Информация о монтируемых в системе дисках лежит в файле "/etc/fstab"
Вот что, например, написано в моём:

/dev/hda6 / ext3 noatime 1 1
none /proc proc defaults 0 0
/dev/hda7 swap swap defaults 0 0

hda6 - основной Linux раздел
hda7 - Linux swap-раздел

Для того чтобы добавить свои NTFS диски необходимо указать:
Что -- Куда -- Тип файловой системы -- Параметры -- 0 0

Здесь "0 0" в конце это тоже параметры, но их можно оставить нулями.
Итак, у меня есть NTFS-раздел hda1. Чтобы добавить его в файл надо написать такую строку:

/dev/hda1 /mnt/win_c ntfs umask=0022,nls=utf8,ro 0 0

Итак, мы монтируем "hda1" в папку "/mnt/win_c" с параметрами "umask=0022,nls=utf8,ro".
umask=0022 - маска доступа к файлам и каталогам. Разрешает всем доступ только на чтение и выполнение, кроме root'a (впрочем неважно, все равно диск на чтение, и даже root ничего не запишет)
nls=utf8 - кодировка, чтобы нормально отображались файлы с русскими буквами в названии. Если с этим возникли проблемы можете указать вместо "nls=utf8" параметр "locale=ru_RU.UTF-8"
ro - ReadOnly, т.е. доступ только на чтение

Важно отметить, что параметры, которые вы задаете, это параметры команды mount, и подробнее о них вы соответственно можете узнать из "man mount"

NTFS на запись

Для того чтобы открыть доступ к NTFS на запись необходимо установить в систему пакет ntfs-3g, который позволит вам записывать информацию на NTFS.
В файл "/etc/fstab" в этом случае необходимо будет добавить такую строку:

/dev/hda1 /mnt/win_c ntfs-3g defaults,umask=0,locale=ru_RU.UTF-8 0 0

Итак. Вместо "ntfs" указали "ntfs-3g".
Параметр "umask=0" разрешает делать с NTFS-диском всё что угодно всем желающим. Не очень верно с точки зрения безопасности, и у многих вместо "umask=0" присутствуют 2 таких параметра: "umask=007,gid=46". В любом случае, один из вариантов, скорее всего должен сработать.

Таким образом после перезагрузки системы Linux должна увидеть и подхватить NTFS-диски на запись или чтение, в зависимости от того, что вы указали в файле.
Если что-то не работает - экспериментируйте с параметрами!

P.S. (добавлено спустя много месяцев):
На самом деле всё проще. Сейчас у меня Дебиан и строка параметров как для NTFS-диска на чтение, так и для NTFS-диска на запись - одинаковая:
defaults,umask=007,gid=46,nls=utf8

- umask разрешает всё владельцу (root надо полагать) и группе; остальным доступ запрещён, ибо нефиг
- группа выставляется номер 46; заметьте, это не имя, а номер. Всех пользователей, которым вы хотите предоставить доступ к NTFS дискам нужно не забыть включить в эту группу в файле "/etc/group". Например, командой:
useradd -G groupname username

Если вам помогла эта статья - оставьте комментарий! Они доступны даже не зарегистрированным пользователям.

За помощь в вопросе спасибо GrayCat, Noki.


Linux. Раздаем интернет в локальную сеть

У меня такая ситуация, что через один и тот же кабель интернет приходит, и через него же надо раздавать интернет в локальную сеть. Вещь абсолютно небезопасная, ну да сейчас не об этом =)

Смысл в том, что Мандрива 2006 позволяла мне такие извращения, а после перехода на 2008 - начала писать, что мол надо иметь 2 сетевых карты. В общем через графический интерфейс (GUI) сделать не получилось. А раздать как-то было надо. Единственным путем решения было использование консоли.

Сейчас я приведу решение, которое совершенно не предназначено только для использования на одной сетевой карты, но под него заточено. Вы легко можете изменить его и сделать так, как вам нужно. Суть от этого не меняется - раздача интернета без использования GUI.

Кроме того, этот код показывает как организовать порт-форвардинг, т.е. перенаправление пришедшего трафика на нужный компьютер внутри сети. В данном случае на внутрисетевом компьютере стоит DC++ Hub (Windows), который не будет работать, если на сервере не организовать перенаправления.

К сожалению я не полностью в этом еще разобрался, но практически все важные строки с моими комментариями. И если вам это интересно - можете полностью вникнуть в суть дела сами.

Итак, достаточно создать текстовый файл, например "inet". Скопировать в него код, который приведен ниже, отредактировать под себя и выполнить его в консоли командой "sh ./inet" (при этом вы должны находиться в папке, где он лежит; т.е. "./" означает, что исполняемый командой "sh" файл лежит в текущей папке)

Скрипт успешно запустился и работал на системах Mandriva 2008.0 и Xubuntu 7.04 (ru).
Если у вас он не запускается - вероятней всего либо:
а) iptables у вас в системе лежат в другой папке; тогда уберите /sbin/ в каждой строке "/sbin/iptables"
б) у вас соединение не ppp0, а dsl0; тогда надо везде ppp0 заменить на dsl0

Для того, чтобы этот код выполнялся каждый раз при включении компьютера сам, и не приходилось делать это руками - его надо добавить в автозагрузку.
Я прописал его в "/etc/rc.d/rc.local" (в OpenSUSE это "/etc/init.d/boot.local"), добавив в конец этого файла строки:
#My script
/home/diman/inet

Пояснения к коду:
ppp0 - PPPoE (инет)
ppp+ - PPPoE (инет), любой - ppp0, ppp1, ppp2 ...
eth0 - локальная сеть
eth+ - локальная сеть, любая - eth0, eth1, eth2, eth3 ...
192.168.23.1 - компьютер в локальной сети (не мой; мой 23.2)

PS (добавлено спустя много месяцев):
Не буду трогать всё то, что написано выше - просто я вернулся сюда подправить код, ну а в итоге всё удалил и привёл свой новый фаервол ^_^
Но сути дела это не меняет.

Так же, набравшись немного опыта, хочу посоветовать вам держать файл в папке /etc, а не в домашней, выставив при этом ему и пользователя и группу root, а всем остальным разрешить только читать его, но не записывать\выполнять. Так безопасней :)

----- ----- ----- ----- -----

#!/bin/sh

# Clear rules
# Чистим правила
iptables -F
iptables -X

# Clear rules additional
# Чистим правила - чтобы уж наверняка
iptables -t nat -F
iptables -t mangle -F

#
# INPFOR
#
# Это наша личная цепочка с именем INPFOR
# Разрешает входящие и forwarding-пакеты, если они относятся к уже установленным соединениям
# Т.е. вашим запущенным приложениям, установившим соединение
#
iptables -N inpfor
iptables -A inpfor -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A inpfor -m state --state NEW ! -i ppp+ -j ACCEPT

# PREROUTING
#
# Прероутинг перенаправляет все входящие пакеты по любому протоколу ppp с dport на to-port
# Если вы, к примеру, хотите запустить web-сервер на 8080 порту и перенаправлять на него с 80 порта - это то, что надо
#
# Второе правило перенаправляет входящие пакеты на другой компьютер. Например, это нужно, если ТАМ на этих портах открыт Torrent\DC-клиент
#
iptables -A PREROUTING -t nat -i ppp+ -p tcp --dport 80 -j REDIRECT --to-port 63080

iptables -A PREROUTING -t nat -p tcp -i ppp0 --dport 1234 -j DNAT --to-destination 192.168.23.1:4321

#
# INPUT
#
# Входящие пакеты.
# По-умолчанию все отбрасываются (iptables -P INPUT DROP)
# Показано как открывать tcp\udp порты\диапазон для ВАШЕГО torrent-клиента
#
# Первые 2 правила запрещают пакетам из вне маскироваться под вашу внутреннюю сеть
# Третье заставляет пройти нашу цепочку правил inpfor
# Четвёртое разрешает входящие с loopback-интерфейса
# Пятое разрешает ICMP-пакеты (ping и прочие сервисы)
#
iptables -I INPUT 1 -i ppp+ -s 127.0.0.0/24 -j REJECT
iptables -I INPUT 1 -i ppp+ -s 192.168.23.0/24 -j REJECT
iptables -A INPUT -j inpfor
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT

# Ваш Torrent-client
iptables -A INPUT -i ppp+ -p tcp --dport 12345:12356 -j ACCEPT
iptables -A INPUT -i ppp+ -p udp --dport 12345 -j ACCEPT

# Чтобы перенаправить пакеты на другой компьютер, их сначала надо разрешить (см. пример в PREROUTING)
iptables -A INPUT -i ppp+ -p tcp --dport 1234 -j ACCEPT

iptables -P INPUT DROP

#
# FORWARD
#
# Форвардинг пакеты. По-умолчанию откидываются.
# Первое правило для перенаправления пакетов на другой комп (см. комментарии в PREROUTING и INPUT)
# Третье правило разрешает перенаправлять с любой сети ppp на eth0
# Пятое - собственно разрешает перенаправление
#
iptables -I FORWARD -p tcp -d 192.168.23.1 --dport 1234 -j ACCEPT
iptables -A FORWARD -j inpfor
iptables -A FORWARD -i ppp+ -o eth0 -j ACCEPT
iptables -P FORWARD DROP
echo 1 > /proc/sys/net/ipv4/ip_forward

#
# OUTPUT
#
# Исходящие. Разрешаем всё :)
#
iptables -P OUTPUT ACCEPT

# Masquerade.
iptables -t nat -A POSTROUTING -o ppp+ -j MASQUERADE

----- ----- ----- ----- -----

Если вам помогла эта статья - оставьте комментарий! Они доступны даже не зарегистрированным пользователям.

За помощь в вопросе огромное спасибо GrayCat.

Linux. ICQ - кракозябры вместо русских букв

Одна из первых проблем с которой приходится сталкиваться при повседневном использовании Линукса.

Допустим у вас есть ICQ-клиент. И в общем-то он даже русифицирован и вы можете писать и отправлять сообщения на русском. Но, как выясняется, некоторые пользователи (а может и все) пишут вам непонятными иероглифами.

Проблема кроется в кодировке. Для того, чтобы все сообщения выглядели нормально необходимо сделать кодировкой по умолчанию CP-1251 Кириллица.
Если у вас Kopete - достаточно выбрать из списка. А если же у вас, например, Pidgin - кодировку нужно будет вписать самому: "cp1251"

Точно так же решается проблема с просмотром текстовых файлов, созданных в Windows - выберите в текстовом редакторе эту кодировку и текст вернется к привычному виду =)

Насчет отправки сообщений в Pidgin по Ctrl+Enter. Решение можно найти здесь.

А чтобы в Pidgin'е появилась проверка русской орфографии надо поставить пакет aspell-ru =)

Если вам помогла эта статья - оставьте комментарий! Они доступны даже не зарегистрированным пользователям.

За помощь в вопросе спасибо Rezerv.