mirror of
https://github.com/arnaucube/echo-botnet.git
synced 2026-02-06 19:06:45 +01:00
works
This commit is contained in:
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
|
||||
botsConfig.json
|
||||
build/botsConfig.json
|
||||
repliesConfig.json
|
||||
keywordsConfig.json
|
||||
temp
|
||||
text.txt
|
||||
40
README.md
40
README.md
@@ -1,2 +1,40 @@
|
||||
# echo-botnet
|
||||
# echo-botnet [](https://goreportcard.com/report/github.com/arnaucode/echo-botnet)
|
||||
A twitter botnet with autonomous bots replying tweets with pre-configured replies
|
||||
|
||||
|
||||
Echo (Ēkhō): https://en.wikipedia.org/wiki/Echo_(mythology)#
|
||||
|
||||
|
||||
Needs the files:
|
||||
```
|
||||
botsConfig.json --> contains the tokens of the bots
|
||||
keywordsConfig.json --> contains the keywords to track from Twitter API
|
||||
repliesConfig.json --> contains the replies
|
||||
```
|
||||
|
||||
configuration file example (botsConfig.json):
|
||||
```
|
||||
[{
|
||||
"title": "account1",
|
||||
"consumer_key": "xxxxxxxxxxxxx",
|
||||
"consumer_secret": "xxxxxxxxxxxxx",
|
||||
"access_token_key": "xxxxxxxxxxxxx",
|
||||
"access_token_secret": "xxxxxxxxxxxxx"
|
||||
},
|
||||
{
|
||||
"title": "account2",
|
||||
"consumer_key": "xxxxxxxxxxxxx",
|
||||
"consumer_secret": "xxxxxxxxxxxxx",
|
||||
"access_token_key": "xxxxxxxxxxxxx",
|
||||
"access_token_secret": "xxxxxxxxxxxxx"
|
||||
},
|
||||
{
|
||||
"title": "account3",
|
||||
"consumer_key": "xxxxxxxxxxxxx",
|
||||
"consumer_secret": "xxxxxxxxxxxxx",
|
||||
"access_token_key": "xxxxxxxxxxxxx",
|
||||
"access_token_secret": "xxxxxxxxxxxxx"
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
22
build/botsConfigDEMO.json
Normal file
22
build/botsConfigDEMO.json
Normal file
@@ -0,0 +1,22 @@
|
||||
[{
|
||||
"title": "account1",
|
||||
"consumer_key": "xxxxxxxxxxxxx",
|
||||
"consumer_secret": "xxxxxxxxxxxxx",
|
||||
"access_token_key": "xxxxxxxxxxxxx",
|
||||
"access_token_secret": "xxxxxxxxxxxxx"
|
||||
},
|
||||
{
|
||||
"title": "account2",
|
||||
"consumer_key": "xxxxxxxxxxxxx",
|
||||
"consumer_secret": "xxxxxxxxxxxxx",
|
||||
"access_token_key": "xxxxxxxxxxxxx",
|
||||
"access_token_secret": "xxxxxxxxxxxxx"
|
||||
},
|
||||
{
|
||||
"title": "account3",
|
||||
"consumer_key": "xxxxxxxxxxxxx",
|
||||
"consumer_secret": "xxxxxxxxxxxxx",
|
||||
"access_token_key": "xxxxxxxxxxxxx",
|
||||
"access_token_secret": "xxxxxxxxxxxxx"
|
||||
}
|
||||
]
|
||||
BIN
build/echo-botnet
Executable file
BIN
build/echo-botnet
Executable file
Binary file not shown.
6
build/keywordsConfigDEMO.json
Normal file
6
build/keywordsConfigDEMO.json
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
"word1",
|
||||
"word2",
|
||||
"wordToTrack3",
|
||||
"word4"
|
||||
]
|
||||
7
build/repliesConfigDEMO.json
Normal file
7
build/repliesConfigDEMO.json
Normal file
@@ -0,0 +1,7 @@
|
||||
[
|
||||
"Reply message 1",
|
||||
"Reply message 2",
|
||||
"Reply message 3",
|
||||
"Hey wassup friend!",
|
||||
"You like the botnet?"
|
||||
]
|
||||
57
color.go
Normal file
57
color.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//Color struct, defines the color
|
||||
type Color struct{}
|
||||
|
||||
var c Color
|
||||
|
||||
//DarkGray color
|
||||
func (c Color) DarkGray(t string) {
|
||||
fmt.Print("\x1b[30;1m") //dark gray
|
||||
fmt.Println(t)
|
||||
fmt.Print("\x1b[0m") //defaultColor
|
||||
}
|
||||
|
||||
//Red color
|
||||
func (c Color) Red(t string) {
|
||||
fmt.Print("\x1b[31;1m") //red
|
||||
fmt.Println(t)
|
||||
fmt.Print("\x1b[0m") //defaultColor
|
||||
}
|
||||
|
||||
//Green color
|
||||
func (c Color) Green(t string) {
|
||||
fmt.Print("\x1b[32;1m") //green
|
||||
fmt.Println(t)
|
||||
fmt.Print("\x1b[0m") //defaultColor
|
||||
}
|
||||
|
||||
//Yellow color
|
||||
func (c Color) Yellow(t string) {
|
||||
fmt.Print("\x1b[33;1m") //yellow
|
||||
fmt.Println(t)
|
||||
fmt.Print("\x1b[0m") //defaultColor
|
||||
}
|
||||
|
||||
//Blue color
|
||||
func (c Color) Blue(t string) {
|
||||
fmt.Print("\x1b[34;1m") //blue
|
||||
fmt.Println(t)
|
||||
fmt.Print("\x1b[0m") //defaultColor
|
||||
}
|
||||
|
||||
//Purple color
|
||||
func (c Color) Purple(t string) {
|
||||
fmt.Print("\x1b[35;1m") //purple
|
||||
fmt.Println(t)
|
||||
fmt.Print("\x1b[0m") //defaultColor
|
||||
}
|
||||
|
||||
//Cyan color
|
||||
func (c Color) Cyan(t string) {
|
||||
fmt.Print("\x1b[36;1m") //cyan
|
||||
fmt.Println(t)
|
||||
fmt.Print("\x1b[0m") //defaultColor
|
||||
}
|
||||
BIN
echo-botnet
Executable file
BIN
echo-botnet
Executable file
Binary file not shown.
28
main.go
Normal file
28
main.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const version = "0.1-dev"
|
||||
|
||||
func main() {
|
||||
c.Yellow("echo-botnet")
|
||||
fmt.Println("---------------")
|
||||
c.Cyan("echo-botnet initialized")
|
||||
c.Purple("https://github.com/arnaucode/echo-botnet")
|
||||
fmt.Println("version " + version)
|
||||
|
||||
keywords := readKeywordsConfig()
|
||||
c.Cyan("keywords configured: ")
|
||||
fmt.Println(keywords)
|
||||
replies := readRepliesConfig()
|
||||
c.Cyan("replies configured: ")
|
||||
fmt.Println(replies)
|
||||
|
||||
fmt.Println("Reading botsConfig.json file")
|
||||
botnet := readConfigTokensAndConnect()
|
||||
c.Cyan("[list of configured bots]:")
|
||||
for _, v := range botnet.ScreenNames {
|
||||
c.Cyan(v)
|
||||
}
|
||||
streamTweets(botnet, keywords, replies)
|
||||
}
|
||||
54
readConfigTokensAndConnect.go
Normal file
54
readConfigTokensAndConnect.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/dghubble/go-twitter/twitter"
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
//Config stores the data from json botsConfig.json file
|
||||
type Config struct {
|
||||
Title string `json:"title"`
|
||||
Consumer_key string `json:"consumer_key"`
|
||||
Consumer_secret string `json:"consumer_secret"`
|
||||
Access_token_key string `json:"access_token_key"`
|
||||
Access_token_secret string `json:"access_token_secret"`
|
||||
}
|
||||
|
||||
//Botnet stores each bot configured
|
||||
type Botnet struct {
|
||||
ScreenNames []string
|
||||
Clients []*twitter.Client
|
||||
}
|
||||
|
||||
func readConfigTokensAndConnect() (botnet Botnet) {
|
||||
var config []Config
|
||||
var clients []*twitter.Client
|
||||
|
||||
file, e := ioutil.ReadFile("botsConfig.json")
|
||||
if e != nil {
|
||||
fmt.Println("error:", e)
|
||||
}
|
||||
content := string(file)
|
||||
json.Unmarshal([]byte(content), &config)
|
||||
fmt.Println("botnetConfig.json read comlete")
|
||||
|
||||
fmt.Print("connecting to twitter api --> ")
|
||||
for i := 0; i < len(config); i++ {
|
||||
configu := oauth1.NewConfig(config[i].Consumer_key, config[i].Consumer_secret)
|
||||
token := oauth1.NewToken(config[i].Access_token_key, config[i].Access_token_secret)
|
||||
httpClient := configu.Client(oauth1.NoContext, token)
|
||||
// twitter client
|
||||
client := twitter.NewClient(httpClient)
|
||||
clients = append(clients, client)
|
||||
botnet.ScreenNames = append(botnet.ScreenNames, config[i].Title)
|
||||
}
|
||||
botnet.Clients = clients
|
||||
|
||||
fmt.Println("connection successfull")
|
||||
|
||||
return botnet
|
||||
}
|
||||
19
readKeywordsConfig.go
Normal file
19
readKeywordsConfig.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func readKeywordsConfig() []string {
|
||||
var keywords []string
|
||||
file, e := ioutil.ReadFile("keywordsConfig.json")
|
||||
if e != nil {
|
||||
fmt.Println("error:", e)
|
||||
}
|
||||
content := string(file)
|
||||
json.Unmarshal([]byte(content), &keywords)
|
||||
|
||||
return keywords
|
||||
}
|
||||
19
readRepliesConfig.go
Normal file
19
readRepliesConfig.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func readRepliesConfig() []string {
|
||||
var replies []string
|
||||
file, e := ioutil.ReadFile("repliesConfig.json")
|
||||
if e != nil {
|
||||
fmt.Println("error:", e)
|
||||
}
|
||||
content := string(file)
|
||||
json.Unmarshal([]byte(content), &replies)
|
||||
|
||||
return replies
|
||||
}
|
||||
108
streamTweets.go
Normal file
108
streamTweets.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dghubble/go-twitter/twitter"
|
||||
)
|
||||
|
||||
func isRT(tweet *twitter.Tweet) bool {
|
||||
tweetWords := strings.Split(tweet.Text, " ")
|
||||
for i := 0; i < len(tweetWords); i++ {
|
||||
if tweetWords[i] == "RT" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func isFromOwnBot(flock Botnet, tweet *twitter.Tweet) bool {
|
||||
for i := 0; i < len(flock.ScreenNames); i++ {
|
||||
if flock.ScreenNames[i] == tweet.User.ScreenName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getRandomReplyFromReplies(replies []string) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
random := rand.Intn(len(replies))
|
||||
return replies[random]
|
||||
}
|
||||
|
||||
func replyTweet(client *twitter.Client, text string, inReplyToStatusID int64) {
|
||||
tweet, httpResp, err := client.Statuses.Update(text, &twitter.StatusUpdateParams{
|
||||
InReplyToStatusID: inReplyToStatusID,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
if httpResp.Status != "200 OK" {
|
||||
c.Red("error: " + httpResp.Status)
|
||||
c.Purple("maybe twitter has blocked the account, CTRL+C, wait 15 minutes and try again")
|
||||
}
|
||||
fmt.Print("tweet posted: ")
|
||||
c.Green(tweet.Text)
|
||||
}
|
||||
|
||||
func startStreaming(botnet Botnet, bot *twitter.Client, botScreenName string, keywords []string, replies []string) {
|
||||
// Convenience Demux demultiplexed stream messages
|
||||
demux := twitter.NewSwitchDemux()
|
||||
demux.Tweet = func(tweet *twitter.Tweet) {
|
||||
if isRT(tweet) == false && isFromOwnBot(botnet, tweet) == false {
|
||||
//processTweet(botnetUser, botScreenName, keywords, tweet)
|
||||
fmt.Println("[bot @" + botScreenName + "] - New tweet detected:")
|
||||
c.Yellow(tweet.Text)
|
||||
reply := getRandomReplyFromReplies(replies)
|
||||
fmt.Print("reply: ")
|
||||
c.Green(reply)
|
||||
fmt.Println(tweet.User.ScreenName)
|
||||
fmt.Println(tweet.ID)
|
||||
replyTweet(bot, "@"+tweet.User.ScreenName+" "+reply, tweet.ID)
|
||||
waitMinutes(1)
|
||||
}
|
||||
}
|
||||
demux.DM = func(dm *twitter.DirectMessage) {
|
||||
fmt.Println(dm.SenderID)
|
||||
}
|
||||
demux.Event = func(event *twitter.Event) {
|
||||
fmt.Printf("%#v\n", event)
|
||||
}
|
||||
|
||||
fmt.Println("Starting Stream...")
|
||||
// FILTER
|
||||
filterParams := &twitter.StreamFilterParams{
|
||||
Track: keywords,
|
||||
StallWarnings: twitter.Bool(true),
|
||||
}
|
||||
stream, err := bot.Streams.Filter(filterParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Receive messages until stopped or stream quits
|
||||
demux.HandleChan(stream.Messages)
|
||||
|
||||
// Wait for SIGINT and SIGTERM (HIT CTRL-C)
|
||||
/*ch := make(chan os.Signal)
|
||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||
log.Println(<-ch)
|
||||
|
||||
fmt.Println("Stopping Stream...")
|
||||
stream.Stop()*/
|
||||
}
|
||||
func streamTweets(botnet Botnet, keywords []string, replies []string) {
|
||||
fmt.Println("total keywords: " + strconv.Itoa(len(keywords)))
|
||||
c.Purple("keywords to follow: ")
|
||||
fmt.Println(keywords)
|
||||
c.Green("Starting to stream tweets")
|
||||
for i := 0; i < len(botnet.Clients); i++ {
|
||||
go startStreaming(botnet, botnet.Clients[i], botnet.ScreenNames[i], keywords, replies)
|
||||
//wait 35 seconds to start the next bot
|
||||
waitSeconds(35)
|
||||
}
|
||||
}
|
||||
22
waitTime.go
Normal file
22
waitTime.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func waitMinutes(minutes int) {
|
||||
//wait to avoid the twitter api limitation
|
||||
timeToSleep := time.Duration(minutes) * time.Minute
|
||||
fmt.Println("waiting " + strconv.Itoa(minutes) + " min to avoid twitter api limitation")
|
||||
fmt.Println(time.Now().Local())
|
||||
time.Sleep(timeToSleep)
|
||||
}
|
||||
func waitSeconds(seconds int) {
|
||||
//wait to avoid the twitter api limitation
|
||||
timeToSleep := time.Duration(seconds) * time.Second
|
||||
fmt.Println("waiting " + strconv.Itoa(seconds) + " seconds to avoid twitter api limitation")
|
||||
fmt.Println(time.Now().Local())
|
||||
time.Sleep(timeToSleep)
|
||||
}
|
||||
Reference in New Issue
Block a user