Make Discord weatherBot with wego package - go

I'm trying to familiarize with Goland and i'd like to make my first bot Discord with it.
I'm making a discord bot and i'm trying to make a weather command using the wego package that's use the forecast's API, here's the package and a screenshot of what it looks like in the readme: here
Here's my code :
package main
import (
"flag"
"fmt"
"github.com/bwmarrin/discordgo"
"github.com/schachmat/ingo"
_ "github.com/schachmat/wego/backends"
_ "github.com/schachmat/wego/frontends"
"github.com/schachmat/wego/iface"
"log"
"os"
"sort"
"strconv"
"strings"
)
var (
commandPrefix string
botID string
)
func pluginLists() {
bEnds := make([]string, 0, len(iface.AllBackends))
for name := range iface.AllBackends {
bEnds = append(bEnds, name)
}
sort.Strings(bEnds)
fEnds := make([]string, 0, len(iface.AllFrontends))
for name := range iface.AllFrontends {
fEnds = append(fEnds, name)
}
sort.Strings(fEnds)
fmt.Fprintln(os.Stderr, "Available backends:", strings.Join(bEnds, ", "))
fmt.Fprintln(os.Stderr, "Available frontends:", strings.Join(fEnds, ", "))
}
func main() {
discord, err := discordgo.New("Bot My_Api_Key")
errCheck("error creating discord session", err)
user, err := discord.User("#me")
errCheck("error retrieving account", err)
botID = user.ID
discord.AddHandler(commandHandler)
discord.AddHandler(func(discord *discordgo.Session, ready *discordgo.Ready) {
err = discord.UpdateStatus(0, "A friendly helpful bot!")
if err != nil {
fmt.Println("Error attempting to set my status")
}
servers := discord.State.Guilds
fmt.Printf("GoBot has started on %d servers", len(servers))
})
err = discord.Open()
errCheck("Error opening connection to Discord", err)
defer discord.Close()
commandPrefix = "!"
<-make(chan struct{})
for _, be := range iface.AllBackends {
be.Setup()
}
for _, fe := range iface.AllFrontends {
fe.Setup()
}
// initialize global flags and default config
location := flag.String("location", "48.839661,2.375300", "`LOCATION` to be queried")
flag.StringVar(location, "l", "48.839661,2.375300", "`LOCATION` to be queried (shorthand)")
numdays := flag.Int("days", 1, "`NUMBER` of days of weather forecast to be displayed")
flag.IntVar(numdays, "d", 1, "`NUMBER` of days of weather forecast to be displayed (shorthand)")
unitSystem := flag.String("units", "metric", "`UNITSYSTEM` to use for output.\n \tChoices are: metric, imperial, si, metric-ms")
flag.StringVar(unitSystem, "u", "metric", "`UNITSYSTEM` to use for output. (shorthand)\n \tChoices are: metric, imperial, si, metric-ms")
selectedBackend := flag.String("backend", "forecast.io", "`BACKEND` to be used")
flag.StringVar(selectedBackend, "b", "forecast.io", "`BACKEND` to be used (shorthand)")
selectedFrontend := flag.String("frontend", "ascii-art-table", "`FRONTEND` to be used")
flag.StringVar(selectedFrontend, "f", "ascii-art-table", "`FRONTEND` to be used (shorthand)")
// print out a list of all backends and frontends in the usage
tmpUsage := flag.Usage
flag.Usage = func() {
tmpUsage()
pluginLists()
}
// read/write config and parse flags
if err := ingo.Parse("wego"); err != nil {
log.Fatalf("Error parsing config: %v", err)
}
// non-flag shortcut arguments overwrite possible flag arguments
for _, arg := range flag.Args() {
if v, err := strconv.Atoi(arg); err == nil && len(arg) == 1 {
*numdays = v
} else {
*location = arg
}
}
// get selected backend and fetch the weather data from it
be, ok := iface.AllBackends[*selectedBackend]
if !ok {
log.Fatalf("Could not find selected backend \"%s\"", *selectedBackend)
}
r := be.Fetch(*location, *numdays)
// set unit system
unit := iface.UnitsMetric
if *unitSystem == "imperial" {
unit = iface.UnitsImperial
} else if *unitSystem == "si" {
unit = iface.UnitsSi
} else if *unitSystem == "metric-ms" {
unit = iface.UnitsMetricMs
}
// get selected frontend and render the weather data with it
fe, ok := iface.AllFrontends[*selectedFrontend]
if !ok {
log.Fatalf("Could not find selected frontend \"%s\"", *selectedFrontend)
}
fe.Render(r, unit)
}
func errCheck(msg string, err error) {
if err != nil {
fmt.Printf("%s: %+v", msg, err)
panic(err)
}
}
func commandHandler(discord *discordgo.Session, message *discordgo.MessageCreate) {
user := message.Author
if user.ID == botID || user.Bot {
//Do nothing because the bot is talking
return
}
if message.Content == "test" {
discord.ChannelMessageSend(message.ChannelID, "test")
}
fmt.Printf("Message: %+v || From: %s\n", message.Message, message.Author)
}
So this does nothing, it just show the message that an user wrote in a channel. I saw that the print of the wego in the terminal is located in src/github.com/schachmat/frontends/emoji.go but i don't know how to use it with my bot, i'd like to print that when an user type "!weather".
I'm lost and don't know what to do.
Sorry for the english, i tried my best, and it's my first post on StackOverflow :)

Related

Go - How can I make sure there aren't empty fields being sent in this code via gRPC?

I am writing some code to send logs with gRPC to a server. The problem I'm having is that the logs are different and not all have every field, but . I'm wondering how I can set only the fields they do have inside logpb.Log short of creating several different types of logs in the proto file?
This is all one methods, but the formatting on StackOverflow isn't with it.
The code:
func (*Client) SendWithgRPC(entry *log.Entry) error {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("Could not connect: %v", err)
}
defer conn.Close()
c := logpb.NewLogServiceClient(conn)
stream, err := c.SendLogs(context.Background())
if err != nil {
log.Fatalf("Error while calling SendLogs: %v", err)
}
logFields := entry.Data
category := ""
debug_id := ""
request_id := ""
tipi := ""
uri := ""
var user_id int32 = 0
ip := ""
if logFields["category"] != nil {
category = logFields["category"].(string)
}
if logFields["debug_id"] != nil {
debug_id = logFields["debug_id"].(string)
}
if logFields["request_id"] != nil {
request_id = logFields["request_id"].(string)
}
if logFields["type"] != nil {
tipi = logFields["type"].(string)
}
if logFields["uri"] != nil {
uri = logFields["uri"].(string)
}
if logFields["user_id"] != nil {
user_id = int32(logFields["user_id"].(int))
}
if logFields["ip"] != nil {
ip = logFields["ip"].(string)
}
logEntry := &logpb.Log{
Time: entry.Time.String(),
Level: entry.Level.String(),
Msg: entry.Message,
Category: category,
DebugId: debug_id,
Ip: ip,
RequestId: request_id,
Type: tipi,
Uri: uri,
Id: user_id,
}
logs := []*logpb.Log{logEntry}
logarray := []*logpb.SendLogsRequest{
&logpb.SendLogsRequest{
Logs: logs,
},
}
fmt.Print(logarray)
for _, req := range logarray {
stream.Send(req)
}
_, err = stream.CloseAndRecv()
if err != nil {
fmt.Printf("Error with response: %v", err)
}
return nil
}
You can use something like https://github.com/go-playground/validator
It offers validation using tags. There is a lot more to this lib apart from basic validation. Do check it out.
Sample example https://github.com/go-playground/validator/blob/master/_examples/simple/main.go
As easy as it gets :
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Name struct {
First string `validate:"required"`
Last string
}
func main() {
validName := Name{First: "John"} // Last name is empty and is not required
invalidName := Name{Last: "Doe"} // first name is empty but is required
validate := validator.New()
err := validate.Struct(invalidName)
if err != nil {
fmt.Println("invalid name struct caused error")
}
err = validate.Struct(validName)
if err != nil {
fmt.Println("valid name struct caused error")
}
fmt.Println("GoodBye")
}
The output of above :
invalid name struct caused error
GoodBye
working code on go-playground : https://play.golang.org/p/S4Yj1yr16t2

binary-to-text encoding that replace a byte into a predefined unique string in golang

I am trying to build a binary-to-text encoder with the ability to replace every byte into a predefined unique string and then use the same string set to decode back into binary.
I am able to make the encoder and decoder for simple .txt files but I want to make this workable for .zip files too.
Encoder :
package main
import (
"archive/zip"
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
)
func main() {
// keys.json is a 256 words dictionary for every byte
keysFile, err := os.Open("keys.json")
if err != nil {
log.Printf("unable to read keys.json file , error : %v", err)
return
}
var keys []string
byteValue, _ := ioutil.ReadAll(keysFile)
if err := json.Unmarshal(byteValue, &keys); err != nil {
log.Printf("unable to unmarshal array , error : %v", err)
return
}
Encoder(keys)
}
func listFiles(file *zip.File) ([]byte, error) {
fileToRead, err := file.Open()
if err != nil {
msg := "Failed to open zip %s for reading: %s"
return nil, fmt.Errorf(msg, file.Name, err)
}
b := make([]byte, file.FileInfo().Size())
fileToRead.Read(b)
defer fileToRead.Close()
if err != nil {
return nil, fmt.Errorf("failed to read zip file : %s for reading , error : %s", file.Name, err)
}
return b, nil
}
func Encoder(keys []string) {
read, err := zip.OpenReader("some.zip")
if err != nil {
msg := "Failed to open: %s"
log.Fatalf(msg, err)
}
defer read.Close()
var encodedBytes []byte
f, err := os.Create("result.txt")
w := bufio.NewWriter(f)
defer f.Close()
for _, file := range read.File {
readBytes, err := listFiles(file)
if err != nil {
log.Fatalf("Failed to read %s from zip: %s", file.Name, err)
continue
}
for i, b := range readBytes {
for _, eb := range []byte(keys[b] + " ") {
encodedBytes = append(encodedBytes, eb)
}
}
}
_, err = w.Write(encodedBytes)
if err != nil {
log.Printf("error :%v", err)
return
}
}
Decoder :
func Decoder(keys []string) {
inputFile, err := os.Open("result.txt")
if err != nil {
log.Printf("unable to read file , error : %v", err)
return
}
inputBytes, _ := ioutil.ReadAll(inputFile)
var (
current []byte
decoded []byte
)
for _, c := range inputBytes {
if c != 32 {
current = append(current, c)
} else {
for i, key := range keys {
if string(current) == key {
decoded = append(decoded, byte(i))
break
}
}
current = []byte{}
}
}
// here i want the decoded back into zip file
}
here is a similar one in nodejs.
Two things:
You are dealing with spaces correctly, but not with newlines.
Your decoder loop is wrong. As far as I can tell, it should look like the following:
for i, key := range keys {
if string(current)==key {
decoded=append(decoded,i)
break
}
}
Also, your decoded is an int-array, not a byte-array.

emersion/go-imap - Mark message as seen

I'm trying to mark messages as seen using this IMAP protocol implementation but It's not working as intended.
I have a function that prints unseen messages and my intention is that by the end, it mark each message as seen.
package main
import (
"emailmonitor/util"
"fmt"
)
func main() {
serverGmail := util.NewServerGmail()
serverGmail.Connect()
serverGmail.Login()
serverGmail.ListUnseenMessages()
}
//-----------------------------------------
package util
import (
"io/ioutil"
"log"
"net/mail"
"net/smtp"
imap "github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
)
type ServerGmail struct {
user string
pass string
erro string
cliente *client.Client
}
func NewServerGmail() *ServerGmail {
serverGmail := &ServerGmail{}
serverGmail.user = "xxxxxx#gmail.com"
serverGmail.pass = "xxxxx"
serverGmail.erro = ""
return serverGmail
}
func (serverGmail *ServerGmail) Connect() {
// Connect to server
cliente, erro := client.DialTLS("smtp.gmail.com:993", nil)
if erro != nil {
serverGmail.erro = erro.Error()
}
log.Println("Connected")
serverGmail.cliente = cliente
}
func (serverGmail *ServerGmail) Login() {
// Login
if erro := serverGmail.cliente.Login(serverGmail.user, serverGmail.pass); erro != nil {
serverGmail.erro = erro.Error()
}
log.Println("Logged")
}
func (serverGmail *ServerGmail) setLabelBox(label string) *imap.MailboxStatus {
mailbox, erro := serverGmail.cliente.Select(label, true)
if erro != nil {
serverGmail.erro = erro.Error()
}
return mailbox
}
func (serverGmail *ServerGmail) ListUnseenMessages() {
// set mailbox to INBOX
serverGmail.setLabelBox("INBOX")
// criteria to search for unseen messages
criteria := imap.NewSearchCriteria()
criteria.WithoutFlags = []string{"\\Seen"}
uids, err := serverGmail.cliente.UidSearch(criteria)
if err != nil {
log.Println(err)
}
seqSet := new(imap.SeqSet)
seqSet.AddNum(uids...)
section := &imap.BodySectionName{}
items := []imap.FetchItem{imap.FetchEnvelope, imap.FetchFlags, imap.FetchInternalDate, section.FetchItem()}
messages := make(chan *imap.Message)
go func() {
if err := serverGmail.cliente.UidFetch(seqSet, items, messages); err != nil {
log.Fatal(err)
}
}()
for message := range messages {
log.Println(message.Uid)
if message == nil {
log.Fatal("Server didn't returned message")
}
r := message.GetBody(section)
if r == nil {
log.Fatal("Server didn't returned message body")
}
// Create a new mail reader
mr, err := mail.CreateReader(r)
if err != nil {
log.Fatal(err)
}
// Print some info about the message
header := mr.Header
if date, err := header.Date(); err == nil {
log.Println("Date:", date)
}
if from, err := header.AddressList("From"); err == nil {
log.Println("From:", from)
}
if to, err := header.AddressList("To"); err == nil {
log.Println("To:", to)
}
if subject, err := header.Subject(); err == nil {
log.Println("Subject:", subject)
}
// MARK "SEEN" ------- STARTS HERE ---------
seqSet.Clear()
seqSet.AddNum(message.Uid)
item := imap.FormatFlagsOp(imap.AddFlags, true)
flags := []interface{}{imap.SeenFlag}
erro := serverGmail.cliente.UidStore(seqSet, item, flags, nil)
if erro != nil {
panic("error!")
}
}
}
Link from Documentation: https://godoc.org/github.com/emersion/go-imap/client#Client.UidStore
Tried to do something similar to Store example.
What can be done to fix it?
modify the following line by changing true to false
mailbox, erro := serverGmail.cliente.Select(label, true)
once you've done this, when the message is fetched (using the UidFetch), it will be automatically marked to "Seen"

Go - Golang openpg - Create Key pair and create signature

I'm currently working on openpgp in combination with golang. I use the following code to generate a new keypair and create a self-signature on the resulting public key:
package main
import (
"bytes"
"crypto"
"time"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
"fmt"
)
//Create ASscii Armor from openpgp.Entity
func PubEntToAsciiArmor(pubEnt *openpgp.Entity) (asciiEntity string) {
gotWriter := bytes.NewBuffer(nil)
wr, errEncode := armor.Encode(gotWriter, openpgp.PublicKeyType, nil)
if errEncode != nil {
fmt.Println("Encoding Armor ", errEncode.Error())
return
}
errSerial := pubEnt.Serialize(wr)
if errSerial != nil {
fmt.Println("Serializing PubKey ", errSerial.Error())
}
errClosing := wr.Close()
if errClosing != nil {
fmt.Println("Closing writer ", errClosing.Error())
}
asciiEntity = gotWriter.String()
return
}
func main() {
var entity *openpgp.Entity
entity, err := openpgp.NewEntity("itis", "test", "itis#itis3.com", nil)
if err != nil {
fmt.Println("ERROR")
}
usrIdstring := ""
for _, uIds := range entity.Identities {
usrIdstring = uIds.Name
}
var priKey = entity.PrivateKey
var sig = new(packet.Signature)
//Prepare sign with our configs/////IS IT A MUST ??
sig.Hash = crypto.SHA1
sig.PubKeyAlgo = priKey.PubKeyAlgo
sig.CreationTime = time.Now()
dur := new(uint32)
*dur = uint32(365 * 24 * 60 * 60)
sig.SigLifetimeSecs = dur //a year
issuerUint := new(uint64)
*issuerUint = priKey.KeyId
sig.IssuerKeyId = issuerUint
sig.SigType = packet.SigTypeGenericCert
err = sig.SignKey(entity.PrimaryKey, entity.PrivateKey, nil)
if err != nil {
fmt.Println("ERROR")
}
err = sig.SignUserId(usrIdstring, entity.PrimaryKey, entity.PrivateKey, nil)
if err != nil {
fmt.Println("ERROR")
}
entity.SignIdentity(usrIdstring, entity, nil)
var copy = entity
var asciiSignedKey = PubEntToAsciiArmor(copy)
fmt.Println(asciiSignedKey)
}
1.) When I serialize the public key (to get an armored version of it), I get the following error message:
Serializing PubKey openpgp: invalid argument: Signature: need to call Sign, SignUserId or SignKey before Serialize
I thought I just used every possible way to create a signature on that key?
2.) I still receive an output from problem 1, when I upload the key to a keyserver, than the available information are incomplete. Only the key-id and the creation date are listed. All additional information like, self-signature, user-id-string and so on are missing (example: https://pgp.mit.edu/pks/lookup?search=0xbe6ee21e94a73ba5&op=index). What went wrong? Is it related to error 1?
PS: I am new to golang, started today.
Maybe this will do what you want. Disclaimer: I am not an expert in openpgp; I don't know whether this is correct or not. But it does work with gpg --import.
package main
import (
"fmt"
"os"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
)
func main() {
var e *openpgp.Entity
e, err := openpgp.NewEntity("itis", "test", "itis#itis3.com", nil)
if err != nil {
fmt.Println(err)
return
}
// Add more identities here if you wish
// Sign all the identities
for _, id := range e.Identities {
err := id.SelfSignature.SignUserId(id.UserId.Id, e.PrimaryKey, e.PrivateKey, nil)
if err != nil {
fmt.Println(err)
return
}
}
w, err := armor.Encode(os.Stdout, openpgp.PublicKeyType, nil)
if err != nil {
fmt.Println(err)
return
}
defer w.Close()
e.Serialize(w)
}
This seems to be a known issue: https://github.com/golang/go/issues/6483. The workaround is to call SerializePrivate first, even if you don't use the result.
I wrote https://github.com/alokmenghrajani/gpgeez for exactly this purpose. It's a Go library which makes things like key creating or exporting a key as an armored string easier.
Here is the gist of it, without any error checking:
func CreateKey() *openpgp.Entity {
key, _ := openpgp.NewEntity(name, comment, email, nil)
for _, id := range key.Identities {
id.SelfSignature.PreferredSymmetric = []uint8{...}
id.SelfSignature.PreferredHash = []uint8{...}
id.SelfSignature.PreferredCompression = []uint8{...}
id.SelfSignature.SignUserId(id.UserId.Id, key.PrimaryKey, key.PrivateKey, nil)
}
// Self-sign the Subkeys
for _, subkey := range key.Subkeys {
subkey.Sig.SignKey(subkey.PublicKey, key.PrivateKey, nil)
}
return r
}

flag redefined - panic searching by key in YouTube data API 3

I am trying to search YouTube video by key like in the golang example. I modified that code a little to let it search by different keys several times.
When I search once it is ok.
func main() {
result1, err1 := SearchYoutubeByKey("hello")
if err1 != nil {
panic(err1)
}
fmt.Println(result1)
// result2, err2 := SearchYoutubeByKey("world")
// if err2 != nil {
// panic(err2)
// }
// fmt.Println(result2)
}
But if I search twice ...
func main() {
result1, err1 := SearchYoutubeByKey("hello")
if err1 != nil {
panic(err1)
}
fmt.Println(result1)
result2, err2 := SearchYoutubeByKey("world")
if err2 != nil {
panic(err2)
}
fmt.Println(result2)
}
... then it panics with error message ...
flag redefined: query
... on line ...
query := flag.String("query", str, "Search term")
Full code:
package main
import (
"code.google.com/p/google-api-go-client/googleapi/transport"
"code.google.com/p/google-api-go-client/youtube/v3"
"flag"
"fmt"
"net/http"
)
var (
maxResults = flag.Int64("max-results", 25, "Max YouTube results")
service *youtube.Service
response *youtube.SearchListResponse
)
const developerKey = "youtube developer key"
type YoutubeSearchResult struct {
Title, YoutubeId string
}
func SearchYoutubeByKey(str string) (result []*YoutubeSearchResult, err error) {
query := flag.String("query", str, "Search term")
flag.Parse()
client := &http.Client{
Transport: &transport.APIKey{Key: developerKey},
}
service, err = youtube.New(client)
if err != nil {
return
}
// Make the API call to YouTube.
call := service.Search.List("id,snippet").
Q(*query).
MaxResults(*maxResults)
response, err = call.Do()
if err != nil {
return
}
// Iterate through each item and add it to the correct list.
for _, item := range response.Items {
switch item.Id.Kind {
case "youtube#video":
result = append(result, &YoutubeSearchResult{Title: item.Snippet.Title, YoutubeId: item.Id.VideoId})
}
}
return
}
func main() {
result1, err1 := SearchYoutubeByKey("hello")
if err1 != nil {
panic(err1)
}
fmt.Println(result1)
result2, err2 := SearchYoutubeByKey("world")
if err2 != nil {
panic(err2)
}
fmt.Println(result2)
}
So it is impossible to use this code on a website. Only the first user will be able to search first time, the others will fail.
I cannot change flag during runtime but how to search by 2 different keys in one program?
Update
working solution:
package main
import (
"code.google.com/p/google-api-go-client/googleapi/transport"
"code.google.com/p/google-api-go-client/youtube/v3"
"flag"
"fmt"
"net/http"
)
var (
maxResults = flag.Int64("max-results", 25, "Max YouTube results")
service *youtube.Service
response *youtube.SearchListResponse
query = flag.String("query", "str", "Search term")
)
const developerKey = "youtube api key"
type YoutubeSearchResult struct {
Title, YoutubeId string
}
func SearchYoutubeByKey(str string) (result []*YoutubeSearchResult, err error) {
flag.Parse()
client := &http.Client{
Transport: &transport.APIKey{Key: developerKey},
}
service, err = youtube.New(client)
if err != nil {
return
}
// Make the API call to YouTube.
call := service.Search.List("id,snippet").
Q(str).
MaxResults(*maxResults)
response, err = call.Do()
if err != nil {
return
}
// Iterate through each item and add it to the correct list.
for _, item := range response.Items {
switch item.Id.Kind {
case "youtube#video":
result = append(result, &YoutubeSearchResult{Title: item.Snippet.Title, YoutubeId: item.Id.VideoId})
}
}
return
}
func main() {
result1, err1 := SearchYoutubeByKey("hello")
if err1 != nil {
panic(err1)
}
fmt.Println(result1)
result2, err2 := SearchYoutubeByKey("world")
if err2 != nil {
panic(err2)
}
fmt.Println(result2)
}
The panic message tells you exactly what's wrong. Your command line flags should be defined only once. If you try to redefine them during runtime, it panics.

Resources