[Tävling] Skapa en simpel IRC-bot i valfritt språk

Permalänk
Medlem

I brist på annat en söndag

import IO import Network import Control.Concurrent import Control.Concurrent.STM import Data.Char (isSpace) whead :: String -> String whead str = let ws = words str in if null ws then str else head ws wtail :: String -> String wtail s = let start = dropWhile isSpace s (_, rest) = break isSpace start in if null rest then "" else tail rest readServer :: TChan String -> TChan String -> Handle -> IO () readServer wCLI wIRC h = do input <- hGetLine h case (whead input) of "PING" -> atomically (writeTChan wIRC ("PONG " ++ (tail $ wtail input) ++ "\r\n")) _ -> atomically (writeTChan wCLI input) readServer wCLI wIRC h writeServer :: TChan String -> Handle -> IO () writeServer rIRC h = do line <- atomically (readTChan rIRC) hPutStrLn h line writeServer rIRC h readClient :: TChan String -> IO () readClient wIRC = do input <- getLine atomically (writeTChan wIRC (input ++ "\r\n")) readClient wIRC writeClient :: TChan String -> IO () writeClient rCLI = do line <- atomically (readTChan rCLI) putStrLn line writeClient rCLI doNothing :: IO () doNothing = do yield doNothing main :: IO () main = do h <- connectTo "irc.quakenet.org" (PortNumber 6667) hSetBuffering h NoBuffering hPutStr h "NICK void-bot\r\n" hPutStr h "USER void-bot foo bar :void-bot\r\n" wIRC <- atomically newTChan rIRC <- atomically (dupTChan wIRC) wCLI <- atomically newTChan rCLI <- atomically (dupTChan wCLI) forkIO (readServer wCLI wIRC h) forkIO (writeServer rIRC h) forkIO (readClient wIRC) forkIO (writeClient rCLI) doNothing

Klarar inte av att joina automatiskt, men det är bara att skriva kommandon manuellt.

Visa signatur

void@qnet
teeworlds, stålverk80, evil schemer, c, c++
Languages shape the way we think, or don't.

Permalänk
Medlem

Först och främst: ursäkta bumpen.

Här är en till variant i Ruby som jag försöker lära mig. Den är lite fulkodad (class IRC < TCPsocket).

#!/usr/local/env ruby require 'socket' server = 'adams.freenode.net' port = '6667' channel = "#aoeuhtns" nickname = 'test_rb' username = 'test_rb' realname = 'Testbot i Ruby' class IRC < TCPsocket def send_command(line) self.write(line + "\n") end def set_nickname(nickname) self.send_command("NICK #{nickname}") end def set_username(username, realname) self.send_command("USER #{username} a b :#{realname}") end def join_channel(channel) self.send_command("JOIN #{channel}") end end s = IRC.new(server, port) s.set_nickname(nickname) s.set_username(username, realname) s.join_channel(channel) while true input = s.gets puts input unless input.split(/ /)[1] == '372' if input[0..3] == 'PING' pong = "PONG " + input.split(/ /)[1].split(/\\/)[0] # Ska vara två \\ mellan //, forumet sabbar. s.send_command(pong) puts pong end end

Finns det någonting jag kan förbättra (kodmässigt, inte featuremässigt)?

Permalänk
Medlem

Jag har skrivit en "gui"-bot i Qt/c++. Just nu connectar den bara, den joinar inte ens någon kanal och man kan inte ge den några kommandon alls men jag jobbar på det. Det här är mitt första program i Qt (bortsett från "hello world" i tutorialen) och min allra första bot.

Permalänk
Medlem

Här är en bot i VB
Koden är byggd för att klara i princip alla IRC-nätverk, även dom som beter sig lite konstigt vad gäller kommunikationen mellan server <-> klient.

Botten kan svara på lite enkla kommandon och meddelanden och den har även lite inlärningsförmåga

Citat:

<@Frigolit> i am your master
< MyIRCBot> Frigolit: I see, so you're my master
<@Frigolit> who is Frigolit?
< MyIRCBot> Frigolit: Frigolit is my master
<@Frigolit> what is the time?
< MyIRCBot> Frigolit: The time is 2007-01-13 12:57:29
<@Frigolit> !part #MyIRCBot
-!- MyIRCBot has left #MyIRCBot

Skapa en Winsock-kontroll, döp den till Sock och skicka in den här koden i formen:

Private Type NickEntry Nickname As String Description As String End Type Dim DataBuffer As String Dim NickArray() As NickEntry Private Sub Form_Load() ReDim NickArray(0) 'Om det inte finns ett index så kommer UBound() smälla senare Sock.RemoteHost = "dk.quakenet.org" Sock.RemotePort = 6667 Sock.Connect 'Anslut till servern End Sub Sub SendData(text) 'Skicka strängen till servern om socketen är ansluten If Sock.State = sckConnected Then Debug.Print "-> " & text: Sock.SendData text & vbCrLf 'Vi slänger även på en radbrytning End Sub Private Sub Sock_Close() 'Anslutningen stängdes, återanslut Sock.Close Sock.Connect End Sub Private Sub Sock_Connect() 'Logga in på nätverket SendData "USER TestClient TestClient TestClient :TestClient" SendData "NICK MyIRCBot" End Sub Private Sub Sock_DataArrival(ByVal bytesTotal As Long) Dim sdata As String Sock.GetData sdata 'Varför behövs följande funktion? 'Jo, för att vissa IRC servrar, som tex asuka (quakenet) kan få för sig att kapa datat och skicka resten i ett annat paket. 'Om det händer så får man inte in all data och kan missa viktiga kommandon som tex en PING-request. For I = 1 To Len(sdata) 'Plocka varje tecken ur paketet C = Mid(sdata, I, 1) If C = Chr(10) Then 'Är det en radbrytning? If DataBuffer <> "" Then 'Är buffern tom? CheckCommand DataBuffer 'Skicka kommandot till CheckCommand DataBuffer = "" 'Töm buffern End If Else If C <> Chr(13) Then DataBuffer = DataBuffer & C 'Lägg tecknet i buffern End If Next I End Sub Private Sub Sock_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean) 'Anslutningsfel, försök igen Sock.Close Sock.Connect End Sub Sub CheckCommand(text) On Error GoTo serr Debug.Print "<- " & text If UCase(Left(text, 4)) = "PING" Then 'Är kommandot en PING-request? SendData "PONG :" & Split(text, ":", 2)(1) Exit Sub End If args = Split(text, " ") If args(0) = "NOTICE" Then Exit Sub 'Skippa "NOTICE AUTH" meddelanden If InStr(1, args(0), "!") Then 'Det är en klient som är källan (:nick!user@host) iNickname = Split(Split(args(0), "!")(0), ":")(1) iUsername = Split(Split(args(0), "@")(0), "!")(1) iHostname = Split(args(0), "@")(1) Else 'Det är servern som är källan (:host) iHostname = Right(args(0), Len(args(0)) - 1) 'Få bort ":" i början av meddelanden End If If UCase(args(1)) = "001" Then 'Server rapporterar att klienten är ansluten utan problem, så här lägger vi ett onconnect-script SendData "JOIN #MyIRCBot" End If If UCase(args(1)) = "PRIVMSG" Then 'Någon skrev ett meddelande iTarget = args(2) iMessage = Split(text, ":", 3)(2) If Left(iTarget, 1) = "#" Then 'Meddelandet skickades till en kanal Debug.Print iNickname & " skrev till " & iTarget & ": " & iMessage msgargs = Split(iMessage, " ") If LCase(msgargs(0)) = "!time" Then SendData "PRIVMSG " & iTarget & " :" & Now If LCase(msgargs(0)) = "!join" And UBound(msgargs) > 0 Then SendData "JOIN " & msgargs(1) If LCase(msgargs(0)) = "!part" And UBound(msgargs) > 0 Then SendData "PART " & msgargs(1) If LCase(msgargs(0)) = "i" Then If LCase(msgargs(1)) = "am" Then iDesc = InvertMessage(Split(iMessage, " ", 3)(2)) AddNickname iNickname, iDesc SendData "PRIVMSG " & iTarget & " :" & iNickname & ": I see, so you're " & iDesc End If ElseIf LCase(msgargs(0)) = "who" Then If LCase(msgargs(1)) = "are" Then If LCase(msgargs(2)) = "you?" Then SendData "PRIVMSG " & iTarget & " :" & iNickname & ": I am an IRC bot." End If ElseIf LCase(msgargs(1)) = "is" Then If Right(msgargs(2), 1) = "?" Then msgargs(2) = Left(msgargs(2), Len(msgargs(2)) - 1) For I = 1 To UBound(NickArray) If LCase(NickArray(I).Nickname) = LCase(msgargs(2)) Then SendData "PRIVMSG " & iTarget & " :" & iNickname & ": " & msgargs(2) & " is " & NickArray(I).Description: Exit Sub Next I SendData "PRIVMSG " & iTarget & " :" & iNickname & ": I have no idea who " & msgargs(2) & " is..." End If End If ElseIf LCase(msgargs(0)) = "what" Then If LCase(msgargs(1)) = "time" Then If LCase(msgargs(2)) = "is" Then If LCase(msgargs(3)) = "it?" Then SendData "PRIVMSG " & iTarget & " :" & iNickname & ": The time is " & Now End If End If End If End If End If Exit Sub serr: Debug.Print "Internal error :: CheckCommand(""" & text & """)" Debug.Print Err.Description Err.Clear End Sub Sub AddNickname(Nickname, Description) For I = 1 To UBound(NickArray) If LCase(NickArray(I).Nickname) = LCase(Nickname) Then NickArray(I).Description = Description 'Vi hittade användaren, uppdatera den... Exit Sub End If Next I ReDim Preserve NickArray(UBound(NickArray) + 1) 'Lägg på ett index på arrayen NickArray(UBound(NickArray)).Nickname = Nickname NickArray(UBound(NickArray)).Description = Description End Sub Function InvertMessage(text) words = Split(text, " ") A = -1 For I = 0 To UBound(words) If LCase(words(I)) = "your" And A <> I Then words(I) = "my": A = I If LCase(words(I)) = "my" And A <> I Then words(I) = "your": A = I If LCase(words(I)) = "i" And A <> I Then words(I) = "you": A = I If LCase(words(I)) = "you" And A <> I Then words(I) = "i": A = I If InvertMessage = "" Then InvertMessage = words(I) Else InvertMessage = InvertMessage & " " & words(I) Next I End Function

Visa signatur

i7-4770K @ 3.5GHz - 32GB RAM - 2x 500GB SSD - 40TB HDD - GTX770

Permalänk
Medlem

gah, blev lite sugen på att koda nu ju!
måste sova först...

jag återkommer

Frigolit: nice hantering av datat ^^

Visa signatur

C#/MonoGame Fanatiker.
Pixel Artist & Game Developer

Permalänk
Medlem

Hej!

Såg tråden när jag kollade runt lite så jag tyckte att jag kunde registrera mig och koda en simpel irc-bot.

Boten är skriven i C++.

Så här ser det ut:

Loggfilen:

Koden:

#include <windows.h> #include <iostream> #include <string> #include <fstream> #define DEFAULT_CHANNEL "#0merta" using namespace std; SOCKET InitSocket(const char* ip, int port); DWORD WINAPI ReceiveThread(); class bot{ public: string name; SOCKET client; string SendToChan(string chan, string data); }; string bot::SendToChan(string chan, string data) { string str = "PRIVMSG "; str+=chan; str+=" :"; str+=data; str+="\r\n"; return str; } bot B; int main() { cout << "Enter name: "; getline(cin, B.name); cout << "Enter IP: "; string ip; getline(cin, ip); cout << "And port: "; int port; cin >> port; cout << "Connecting to " << ip << " on port " << port << endl; B.client = InitSocket(ip.c_str(), port); CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(ReceiveThread), NULL, 0, 0); string nick = "NICK "; nick += B.name; nick += "\r\nUSER hejsan B B B\r\n"; send(B.client, nick.c_str(), nick.length(), 0); Sleep(2000); string Join = "JOIN "; Join += DEFAULT_CHANNEL; Join += "\r\n"; send(B.client, Join.c_str(), Join.length(), 0); send(B.client, B.SendToChan(DEFAULT_CHANNEL, "Test").c_str(), B.SendToChan(DEFAULT_CHANNEL, "Test").length(), 0); while(1) { cout << "Kanal/person du vill skicka meddelande till: "; string buffer; getline(cin, buffer); cout << "Meddelande: "; string med; getline(cin, med); send(B.client, B.SendToChan(buffer, med).c_str(), B.SendToChan(buffer, med).length(), 0); } } void AddToLog(std::string buffer) { ofstream stream; stream.open("C:\\logfile.txt", ios::app); stream << buffer.c_str(); stream.close(); } DWORD WINAPI ReceiveThread() { char data[1024] = ""; while(1) { while(recv(B.client, data, sizeof(data), 0) > 0) { AddToLog(data); if(strstr(data, "PING") != NULL) { std::string test = data; std::string substring = test.substr(6); test = "PONG :"; test += substring; test += "\r\n"; send(B.client, test.c_str(), test.length(), 0); } ZeroMemory(data, sizeof(data)); } Sleep(10); } return 0; } SOCKET InitSocket(const char* ip, int port) { WSADATA wsaData; WORD version; SOCKET zeSock; version = MAKEWORD(2, 2); int error; error = WSAStartup(version, &wsaData); if(error != 0) return false; if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); return false; } zeSock = socket(AF_INET, SOCK_STREAM, 0); if(zeSock == INVALID_SOCKET) return false; struct hostent *host; struct sockaddr_in sin; host = gethostbyname(ip); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr = *((in_addr *)host->h_addr); sin.sin_port = htons(port); error = connect(zeSock, (sockaddr*)&sin, sizeof sin); if(error != 0) return false; return zeSock; }

Tack för mig!

/Henric

Visa signatur

C++

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av Kekke3
text!

OffT!
Noterade att det är inte bara i IRC du bottar:).

Permalänk
Medlem

Hehe, sant

Visa signatur

C++

Permalänk
Medlem

Början på en IRC-modul i Ruby, riktigt trevligt språk faktiskt.

#!/usr/bin/ruby require 'socket' require 'yaml' module IRC def self.new Connection.new end class User attr_reader :nickname, :username, :realname, :channels, :hostname, :port def initialize begin config = YAML.load_file("config.yml") rescue Errno::ENOENT puts "File config.yml missing, exiting..." exit end @nickname = config['nickname'] @username = config['username'] @realname = config['realname'] @channels = config['channels'] @hostname = config['hostname'] @port = config['port'] end end class IRCsocket < TCPsocket def send(line) self.write(line + "\n") end def output @output = self.gets.split(/:/)[1..-1] rescue nil end def connection_status begin status = self.gets.split(/ /)[1][0..2] rescue NoMethodError status = nil end status end end class Connection attr_reader :socket def initialize @user = User.new self.connect_to_server end def connect_to_server begin @socket = IRCsocket.new(@user.hostname, @user.port) rescue SocketError sleep 10 retry end @socket.send("NICK #{@user.nickname}") @socket.send("USER #{@user.username} aoeu * :#{@user.realname}") until @socket.connection_status == '004' puts @socket.output end @user.channels.each { |channel| @socket.send("JOIN ##{channel}") } end end end exit unless __FILE__ == $0 irc = IRC.new loop { puts irc.socket.output }

config.yml

--- nickname: rb_bot username: rb_bot realname: Ruby Bot hostname: irc.freenode.net port: 6667 channels: - rb_bot

Edit: och så en kortare version

require 'socket' s = TCPsocket.new('irc.freenode.net', 6667) s.write("NICK rubybot\nUSER rubybot aoeu * :rubybot\n") loop { p s.gets }