Ir para conteúdo
Entre para seguir isso  
Eventide

IRC integrado com OTserv

Recommended Posts

Eventide    7
Eventide

Lembrando que todos os créditos para o código vão para o Nostradamus e Fusion!(e traduzido por Evesys)

esse código vai funcionar em Open Tibia 0.6.1 e talvez 0.6.2, para usar em TFS será preciso algumas modificações..

 

[O que é isso?]

 

É um bot de IRC que se integra ao Tibia, tendo um canal exclusivo para isso, aonde mensagens que saem do irc entram no canal, e mensagens que saem do canal entram no irc atravez do bot, podendo ainda se fazer scripts em Lua para remotes!

 

[Código]

 

crie um novo arquivo chamado IRCBot.cpp, e nele coloque:

#include "otpch.h"

#ifdef __ELEMENTARIA_IRCBOT__

#include <boost/bind.hpp>
#include <malloc.h>
#include <sstream>

#include "chat.h"
#include "IRCBot.h"
#include "tasks.h"

extern Chat g_chat;

IRCBot::IRCBot(const uint16_t channelid, const char* name, const char* password, const char* server, const uint16_t port, const char* channel)
   : m_chatchannel(NULL), m_name(name), m_password(password), m_server(server), m_port(port), m_channel(channel), m_socket(-1)
{
   m_chatchannel = g_chat.CreateChannel(channelid, "IRC - " + std::string(m_channel));
   run();
}

IRCBot::~IRCBot()
{
   g_chat.DeleteChannel(m_chatchannel);
   stop();
}

void IRCBot::run()
{
   ircConnect();
}

void IRCBot::stop()
{
   ircSend("QUIT :a revolution is close to begin!");
   closesocket(m_socket); 
}

OTSYS_THREAD_RETURN IRCBot::ircRead(void* data)
{
   IRCBot* _this = (IRCBot*)data;

   char buffer[512];
   char *result;

   while(1)
   {
       memset(buffer, 0, sizeof(buffer));
       if(recv(_this->m_socket, buffer, sizeof(buffer), 0) <= 0)
       {
           closesocket(_this->m_socket);
           _this->ircConnect();
       }

       if((result = strtok(buffer, "\n")) != NULL)
       {
           do
           {
               if(!strncasecmp(result, "PING :", 6))
               {
                   result[1] = 'O';
                   _this->ircSend("%s", result);
                   _this->joinChannel(_this->m_channel);
               }
               else if(strstr(result, " 376 "))
                   _this->joinChannel(_this->m_channel);
               else
                   _this->parseMessage(result); 
           }
           while((result = strtok(NULL, "\n")) != NULL);
       }
   }
}

void IRCBot::ircConnect()
{
   if(CreateIRCSocketAndConnect() == 0)
       return;

   ircSend("USER %s . . :%s\r\n\0", m_name, m_name);
   ircSend("NICK %s\r\n\0", m_name);
   ircSend("PRIVMSG NICKSERV :IDENTIFY %s\r\n\0", m_password);
   OTSYS_CREATE_THREAD(IRCBot::ircRead, this);
}

int IRCBot::CreateIRCSocketAndConnect()
{
   struct sockaddr_in addr;

   memset(&addr, 0, sizeof(addr));

   addr.sin_addr.s_addr = inet_addr(host_addr(m_server));
   addr.sin_family = AF_INET;
   addr.sin_port = htons(m_port);

   if((m_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
       return 0;

   if(connect(m_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
   {
       closesocket(m_socket);
       return 0;
   }
   return 1;
}

void IRCBot::ircSend(char *Format, ...)
{
   va_list va;
   char buf[512];
   va_start(va, Format);
   vsprintf(buf, Format, va);
   va_end(va);
   send(m_socket, buf, strlen(buf), 0);
}

void IRCBot::sendMessage(char *to, char *message)
{
   ircSend("PRIVMSG %s :%s\r\n\0", to, message);
}

void IRCBot::sendMessage(Player* player, std::string message)
{
   std::stringstream tosend;
   tosend << player->getName() << "[" << player->getLevel() << "]: " << message;
   sendMessage(tosend.str().c_str());
}

void IRCBot::sendMessage(const char* message)
{
   ircSend("PRIVMSG %s :%s\r\n\0", m_channel, message);
}

void IRCBot::joinChannel(const char *channel)
{
   ircSend("JOIN %s\r\n\0", channel);
}

void IRCBot::parseMessage(char *buffer)
{
   char *buf = (char*)alloca(strlen(buffer) + 1);
   char nick[256], user[256], host[256], message[256];
   int len;

   if(strstr(buffer, " 451 * ") && !strstr(buffer, " PRIVMSG "))
   {
       ircSend("USER %s . . :%s\r\n\0", m_name, m_name);
       ircSend("NICK %s\r\n\0", m_name);
       return;
   }

   if(strstr(buffer, " 433 * ") && !strstr(buffer, " PRIVMSG "))
   {
       ircSend("PRIVMSG NICKSERV :GHOST %s %s\r\n\0", m_name, m_password);
       ircSend("NICK %s\r\n\0", m_name);
       ircSend("PRIVMSG NICKSERV :IDENTIFY %s\r\n\0", m_password);
       return;
   }

   if((strstr(buffer, "PRIVMSG") == NULL) || (strncmp(buffer,":",1)))
       return;
   strcpy(buf, buffer+1);

   memset(&nick,0,sizeof(nick));
   memset(&user,0,sizeof(user));
   memset(&host,0,sizeof(host));
   memset(&message,0,sizeof(message));

   if((len = strstr(buf, "!") - buf) < 1)
       return;
   strncpy(nick, buf, len);
   buf = buf+len+1;

   if((len = strstr(buf, "@") - buf) < 1)
       return;
   strncpy(user, buf, len);
   buf = buf+len+1;

   if((len = strstr(buf, " ") - buf) < 1)
       return;
   strncpy(host, buf, len);
   buf = buf+len+1;

   if((len = strstr(buf, ":") - buf) < 1)
       return;
   buf = buf+len+1;
   strncpy(message,buf,strlen(buf)-1);

   parseCommand(message, nick, user, host);
   return;
}

void IRCBot::parseCommand(char *buffer, char *nick, char *user, char *host)
{
   std::stringstream ss;
   ss << "[iRC] " << nick;
   ChatChannel *ircChannel = g_chat.getChannel(0, m_chatchannel->getId());
   ircChannel->talkFromIRC(ss.str(), buffer);
   return;
}

char* IRCBot::host_addr(const char *addr)
{
   struct hostent *he = NULL;
   char address[64];

   if (addr == NULL)
       strcpy(address, "");
   else
       strcpy(address, addr);
   he = gethostbyname(address);
   if(he == NULL)
       return NULL;

   return inet_ntoa(*(struct in_addr *) he->h_addr_list[0]);
}

#endif

 

agora crie um arquivo chamado IRCBot.h, e coloque nele:

#ifdef __ELEMENTARIA_IRCBOT__
#ifndef __IRCBOT_H__
#define __IRCBOT_H__

class ChatChannel;
class Player;

class IRCBot
{
public:
   IRCBot(const uint16_t channelid, const char* name, const char* password, const char* server, const uint16_t port, const char* channel);
   ~IRCBot();

   ChatChannel* getChatChannel()
   {
       return m_chatchannel;
   }

   const char* getName() const
   {
       return m_name;
   }

   const char* getPassword() const
   {
       return m_password;
   }

   const char* getServer() const
   {
       return m_server;
   }

   const uint16_t getPort() const
   {
       return m_port;
   }

   const char* getChannel() const
   {
       return m_channel;
   }

   bool isConnected() const
   {
        return m_socket >= 0;
   }

   void run();
   void stop();

   static OTSYS_THREAD_RETURN ircRead(void* data);
   void ircSend(char *Format, ...);
   int CreateIRCSocketAndConnect();
   void ircConnect();
   char *host_addr(const char *addr);
   void joinChannel(const char *channel);
   void sendMessage(char *to, char *message);
   void sendMessage(Player* player, std::string message);
   void sendMessage(const char* message);
   void parseMessage(char *buffer);
   void parseCommand(char *buffer, char *nick, char *user, char *host);

private:
   ChatChannel* m_chatchannel;

   const char* m_name;
   const char* m_password;

   const char* m_server;
   const uint16_t m_port;
   const char* m_channel;

   int m_socket;
};

#endif
#endif

 

crie um novo arquivo chamado IRCEvents.cpp e inclua nele:

#include "otpch.h"

#ifdef __ELEMENTARIA_IRCBOT__
#include "IRCBot.h"
#include "IRCEvents.h"

extern IRCBot* g_IRCBot;

#include "tools.h"

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "IRCEvents.h"

IRCEvents::IRCEvents() :
m_scriptInterface("IRCEvent Interface")
{
   m_scriptInterface.initState();
}

IRCEvents::~IRCEvents()
{
   clear();
}

void IRCEvents::clear()
{
   IRCEventList::iterator it = eventsMap.begin();
   while(it != eventsMap.end())
   {
       delete it->second;
       eventsMap.erase(it);
       it = eventsMap.begin();
   }

   m_scriptInterface.reInitState();
}

LuaScriptInterface& IRCEvents::getScriptInterface()
{
   return m_scriptInterface;
}

std::string IRCEvents::getScriptBaseName()
{
   return "ircevents";
}

Event* IRCEvents::getEvent(const std::string& nodeName)
{
   if(asLowerCaseString(nodeName) == "ircevent")
       return new IRCEvent(&m_scriptInterface);
   else
       return NULL;
}

bool IRCEvents::registerEvent(Event* event, xmlNodePtr p)
{
   IRCEvent* ircEvent = dynamic_cast<IRCEvent*>(event);
   if(!ircEvent)
       return false;

   eventsMap.push_back(std::make_pair(ircEvent->getType(), ircEvent));
   return true;
}

bool IRCEvents::onText(const char* who, const char* message)
{
   for(IRCEventList::iterator it = eventsMap.begin(); it != eventsMap.end(); ++it)
   {
       if(it->first == IRCEVENT_ONTEXT)
       {
           IRCEvent* ircEvent = it->second;
           if(!ircEvent->executeText(who, message, g_IRCBot->getChannel()))
               return false;
       }
   }

   return true;
}

IRCEvent::IRCEvent(LuaScriptInterface* _interface) : Event(_interface)
{
   //
}

bool IRCEvent::configureEvent(xmlNodePtr p)
{
   std::string strValue;

   if(readXMLString(p, "type", strValue))
   {
       std::string tmpStr = asLowerCaseString(strValue);
       if(tmpStr == "text")
           m_type = IRCEVENT_ONTEXT;
       else
       {
           std::cout << "[Error - IRCEvent::configureEvent] Unkown type for IRCEvent." << std::endl;
           return false;
       }
   }
   else
   {
       std::cout << "[Error - IRCEvent::configureEvent] No type for IRCEvent." << std::endl;
       return false;
   }

   return true;
}

std::string IRCEvent::getScriptEventName()
{
   switch(m_type)
   {
       case IRCEVENT_ONTEXT:
           return "onText";

       default:
           break;
   }

   return "";
}

int32_t IRCEvent::executeText(const char* who, const char* message, const char* channel)
{
   //onText(who, message, channel)
   if(m_scriptInterface->reserveScriptEnv())
   {
       ScriptEnviroment* env = m_scriptInterface->getScriptEnv();

       #ifdef __DEBUG_LUASCRIPTS__
       char desc[125];
       sprintf(desc, "%s - %i (%i)", getName().c_str(), interval, lastExecution);
       env->setEventDesc(desc);
       #endif

       env->setScriptId(m_scriptId, m_scriptInterface);

       lua_State* L = m_scriptInterface->getLuaState();

       m_scriptInterface->pushFunction(m_scriptId);
       lua_pushstring(L, who);
       lua_pushstring(L, message);
       lua_pushstring(L, channel);

       int32_t result = m_scriptInterface->callFunction(3);
       m_scriptInterface->releaseScriptEnv();

       return (result == LUA_TRUE);
   }
   else
   {
       std::cout << "[Error] Call stack overflow. IRCEvent::executeText" << std::endl;
       return 0;
   }
}

#endif

 

agora crie um arquivo chamado IRCEvents.h e nele coloque:

#ifdef __ELEMENTARIA_IRCBOT__

#ifndef __IRCEVENT_H__
#define __IRCEVENT_H__

#include <list>
#include <string>
#include "luascript.h"
#include "baseevents.h"
#include "const.h"
#include "scheduler.h"

enum IRCEventType_t
{
   IRCEVENT_NONE,
   IRCEVENT_ONTEXT
};

class IRCEvent;

class IRCEvents : public BaseEvents
{
   public:
       IRCEvents();
       virtual ~IRCEvents();

       bool onText(const char* who, const char* message);

   protected:
       virtual LuaScriptInterface& getScriptInterface();
       virtual std::string getScriptBaseName();
       virtual Event* getEvent(const std::string& nodeName);
       virtual bool registerEvent(Event* event, xmlNodePtr p);
       virtual void clear();

       typedef std::list< std::pair<IRCEventType_t, IRCEvent* > > IRCEventList;
       IRCEventList eventsMap;

       LuaScriptInterface m_scriptInterface;
};

class IRCEvent : public Event
{
   public:
       IRCEvent(LuaScriptInterface* _interface);
       virtual ~IRCEvent() {}

       virtual bool configureEvent(xmlNodePtr p);

       IRCEventType_t getType() const {return m_type;}

       //scripting
       int32_t executeText(const char* who, const char* message, const char* channel);

   protected:
       virtual std::string getScriptEventName();

       IRCEventType_t m_type;
};

#endif

#endif

 

agora no arquivo otserv.cpp, após:

Vocations g_vocations;

 

coloque:

#ifdef __ELEMENTARIA_IRCBOT__
   #include "IRCBot.h"
   IRCBot* g_IRCBot;
#endif

 

e depois de:

if(!g_config.loadFile(ConfigManager::filename))
       startupErrorMessage("Unable to load " + ConfigManager::filename + "!");

 

coloque:

#ifdef __ELEMENTARIA_IRCBOT__
   g_IRCBot = new IRCBot(
                           0x0A,
                           g_config.getString(ConfigManager::IRCBOT_NAME).c_str(),
                           g_config.getString(ConfigManager::IRCBOT_PASSWORD).c_str(),
                           g_config.getString(ConfigManager::IRCBOT_SERVER).c_str(),
                           (uint16_t)g_config.getNumber(ConfigManager::IRCBOT_PORT),
                           g_config.getString(ConfigManager::IRCBOT_CHANNEL).c_str()
                       );
#endif

 

em configmanager.cpp apos:

m_confNumber[LOGIN_PROTECTION] = getGlobalNumber(L, "loginProtectionPeriod", 10);

 

coloque:

#ifdef __ELEMENTARIA_IRCBOT__
   m_confString[iRCBOT_NAME] = getGlobalStringField(L, "IRCBot", "name", "ElementariaBot");
   m_confString[iRCBOT_PASSWORD] = getGlobalStringField(L, "IRCBot", "password", "elementariarulez");
   m_confString[iRCBOT_SERVER] = getGlobalStringField(L, "IRCBot", "server", "irc.quakenet.org");
   m_confNumber[iRCBOT_PORT] = getGlobalNumberField(L, "IRCBot", "port", 6667);
   m_confString[iRCBOT_CHANNEL] = getGlobalStringField(L, "IRCBot", "channel", "#elementaria");
#endif

 

em configmanager.h depois de:

ERROR_LOG

 

coloque:

#ifdef __ELEMENTARIA_IRCBOT__
           IRCBOT_NAME,
           IRCBOT_PASSWORD,
           IRCBOT_SERVER,
           IRCBOT_CHANNEL,
#endif

 

Como esse código trabalha com arrays no config.lua, você deverá adicionar suporte a isso, já que por padrão não tem, coloque isso em um lugar vazio do configmanager.cpp:

// Get fields from config.

std::string ConfigManager::getGlobalStringField (lua_State* _L, const std::string& _identifier, const std::string& _key, const std::string& _default)
{
   lua_getglobal(_L, _identifier.c_str()); /* get the table variable */
   if(!lua_istable(_L, -1)) /* check if is a table */
       return _default;

   lua_pushstring(_L, _key.c_str()); /* push the name of the field we want onto the stack */
   lua_gettable(_L, -2); /* get the field which we gave the name preveously */

   if(!lua_isstring(_L, -1)) /* check if we are threating with a string */
       return _default;

   std::string result = std::string(lua_tostring(_L, -1)); /* get the result */
   lua_pop(_L, 2); /* pop the table and the result */

   return result;
}

 

em configmanager.h na parte "private" da classe, adicione:

std::string getGlobalStringField (lua_State* _L, const std::string& _identifier, const std::string& _key, const std::string& _default = "");

 

em protocolgame.cpp antes de qualquer "#include", adicione:

#ifdef __ELEMENTARIA_IRCBOT__
   #include "IRCBot.h"
   extern IRCBot *g_IRCBot;
#endif

 

e no final do arquivo isto:

#ifdef __ELEMENTARIA_IRCBOT__
void ProtocolGame::addMessageFromIRC(std::string name, std::string text)
{
   NetworkMessage* msg = getOutputBuffer();
   if(!msg)
       return;

   TRACK_MESSAGE(msg);

   msg->AddByte(0xAA);
   msg->AddU32(0x00);
   msg->AddString(name);
   msg->AddU16(0x00);
   msg->AddByte(SPEAK_CHANNEL_Y);
   msg->AddU16(g_IRCBot->getChatChannel()->getId());
   msg->AddString(text);
}
#endif

 

em protocolgame.h declare na classe:

#ifdef __ELEMENTARIA_IRCBOT__
       void addMessageFromIRC(std::string name, std::string text);
#endif

 

em chat.cpp depois de qualquer "#include" adicione:

#ifdef __ELEMENTARIA_IRCBOT__
   #include "IRCBot.h"
   #include "IRCEvents.h"

   extern IRCBot* g_IRCBot;
   extern IRCEvents* g_IRCEvents;
#endif

 

e antes de:

return channel->talk(player, type, text);

 

coloque:

#ifdef __ELEMENTARIA_IRCBOT__
   if(g_IRCBot && channelId == g_IRCBot->getChatChannel()->getId())
   {
       g_IRCBot->sendMessage(player, text);
       type = SPEAK_CHANNEL_R1;
   }
#endif

 

agora no final do arquivo:

#ifdef __ELEMENTARIA_IRCBOT__
void ChatChannel::talkFromIRC(std::string nick, std::string text)
{
   if(!g_IRCEvents->onText(nick.c_str(), text.c_str()))
       return;

   for(UsersList::iterator it = m_users.begin(); it != m_users.end(); ++it)
   {
       Player* toPlayer = g_game.getPlayerByID(*it);
       if(toPlayer)
           toPlayer->addMessageFromIRC(nick, text);
   }
}
#endif

 

em scriptmanager.cpp, depois dos "#include":

#ifdef __ELEMENTARIA_IRCBOT__
   #include "IRCEvents.h"
   IRCEvents* g_IRCEvents = NULL;
#endif

 

e depois de:

   g_moveEvents = new MoveEvents();
   if(!g_moveEvents->loadFromXml())
   {
       std::cout << "> ERROR: Unable to load MoveEvents!" << std::endl;
       return false;
   }

 

coloque:

#ifdef __ELEMENTARIA_IRCBOT__
   g_IRCEvents = new IRCEvents();
   if(!g_IRCEvents->loadFromXml())
   {
       std::cout << "> ERROR: Unable to load IRCEvents!" << std::endl;
       return false;
   }
#endif

 

em definitions.h no final do arquivo coloque:

#define __ELEMENTARIA_IRCBOT__

 

[Finalizando]

 

agora é só incluir os arquivos criados(IRCBot.cpp, IRCBot.h, IRCEvents.cpp e IRCEvents.h) no projeto e compilar.

sendo que tiver algum erro de projeção ou de função não declarada, é só declarar a função, e por favor reporte aqui que eu arrumo no código.(eu lembro que teve erro assim mas não lembro onde direito)

 

não se esqueça de criar dentro da pasta data outra pasta seguindo o mesmo jeito das outras pastas de script

 

e no config.lua vai ficar assim

IRCBot = {
name = "nome do seu bot",
password = "senha",
server = "irc.otserv.com.br", -- coloque sua rede de preferencia!
port = 6667, -- porta padrão de irc
channel = "#otnet" -- canal de sua preferencia!
}

Editado por Eventide

Compartilhar este post


Link para o post
Fletch    0
Fletch

é legal pra caramba, ele transforma um channel no tibia em um irc?

Compartilhar este post


Link para o post
Eventide    7
Eventide

não exatamente, ele faz conexão entre o Tibia e o IRC, transformando o canal #irc do Tibia em uma extensão de determinado canal na rede configurada, sendo que tudo que é dito no tibia vai pro canal e tudo que é dito no canal é dito no Tibia, por meio desse bot!

Compartilhar este post


Link para o post
Elys Zarox    0
Elys Zarox

Oo < realmente Oo pensava que isso era feito em .lua com alguma conexão com o irc.

e quais as modificações que é presiso fazer para funcionar em TFS?

 

quem nao intendeu entra no pharenight server que lá está funcionando isso mais o pharenight está em manutenção no momento :P

Editado por Elys Zarox

Compartilhar este post


Link para o post
Mock    32
Mock

ah que otimo eventide vc destruiu meu IRC bot em lua xD

Compartilhar este post


Link para o post
The Death Eyes    0
The Death Eyes

kkkk muito bom amo esse cara :D

 

MOCK: VOCÊ É VOCE TU É O CARA :D ELE APENAS COPIO VOCE FEZ

Compartilhar este post


Link para o post
Deragon    25
Deragon

/\

nao foi o nostradamus e o fusion que fizeram ? --'

Editado por Deragon

Compartilhar este post


Link para o post
Mock    32
Mock

o nostra fez o dele,

eu fiz o meu,

eventide fez o seu.

:s

a diferença que eu e eventide não somos mão de vaca e postamos para as pessoas.

Compartilhar este post


Link para o post
Eventide    7
Eventide

esse ai é o do Nostradamus e do Fusion, não o meu..

o meu(quem spaliou?) não está pronto e não vai ser integrado ao Tibia, mas terá a ver com o fórum..

data de lançamento para 2014, logo apos o lançamento do Apoca e um dia antes da olimpiada no brasil e da copa do mundo no kongo

 

@elys

esse código é diferente do que o Mock tem, o do Mock não é integrado..

a não ser que esse que o phare tenha seja diferente do que ele liberou ai.. ;o~

Compartilhar este post


Link para o post
Mock    32
Mock

o0 pensei q esse code vc q tinah feito e nao o nostra.

bom q seje de qualquer jeito o meu bot seria para quem nao quer ficar mudando a source mesmo pro que o TFS que tem bra baixar sempre é diferente do TFS das sourcers.

Compartilhar este post


Link para o post
Zephiroth.    0
Zephiroth.

O_O

Perfeito, amei *_____*

Compartilhar este post


Link para o post
Fixthow    33
Fixthow

Quais modificações seria necessario para TFS? Acho que se possivel postar seria bom, pois a maioria dos servidores hoje em dia sã basiados em TFS, e maioria do povo não entende de Programação ;x

Só Uma opnião :D

 

P.s: amei <3, concerteza se funcionar em TFS irei usar :D

Compartilhar este post


Link para o post
Eventide    7
Eventide

de qualquer jeito vai ter que entender de programação pra usar este código, pelo menos o básico para compilar.. ;o

 

acho que o principal que vai mudar para TFS é o local aonde se coloca os códigos, e talvez alguns bugs principalmente nos eventos, pois essa parte é um pouco diferente em TFS. o código do TFS é todo alterado e estranho, ele é péssimo pra quem programa ou pega códigos prontos para por no seu servidor, e ainda pode rolar probleminhas com o linker(transformação dos objetos em um executavel), o que é MUITO³ chato de arrumar.

Compartilhar este post


Link para o post
Raphael Carnaúba    1
Raphael Carnaúba

Eventide, coloca os creditos em negrito :D

 

"Todos os créditos do código vão a autoria de Nostradamus e Fusion (tradução realizada por EveSys)"

 

Ah, e parabéns pelo cargo tão desejado :) Você merecia!

Compartilhar este post


Link para o post
KwiiBy~    0
KwiiBy~

Muito legal deve ter dado um trabalhão ;P

Compartilhar este post


Link para o post
Eventide    7
Eventide

Pra mim nenhum, foram o Nostradamus e o Fusion que fizeram. 'o'

 

@rapha

pelo visto vou ter que fazer isso mesmo.. ;/

Compartilhar este post


Link para o post
lehdarkangel    0
lehdarkangel

Great Man // Congratulations ~

Compartilhar este post


Link para o post
flipagenor    0
flipagenor

Caramba, muito bem feito! :D

Parabéns

Compartilhar este post


Link para o post
Visitante
Este tópico está impedido de receber novos posts.
Entre para seguir isso  
  • Quem Está Navegando   0 membros estão online

    Nenhum usuário registrado visualizando esta página.

×