me

notes from mnlwldr

Thoughts about programming and other things I want to share

@mnlwldr on Twitter @mnlwldr on GitHub @mnlwldr on Mastodon mnlwldr@gmail.com defer.cc

golang

Posts tagged as golang

I published a Go application today to export the last tweets from your twitter timeline to your local hugo repository by using the Twitter API. You can find a copy of my tweets here, mostly written in German.

twitter2hugo-export on GitHub.

Tags: #golang #twitter #indieweb

Find the longest string or integer in a SHA1 Hash (or whatever). It’s just sha-mazing and without any sense.

package shamazing

import (
	"regexp"
	"strconv"
)

// FindLongestString will retrieve a string like a SHA1, MD5 or whatever
// and return the longest string (first one)
func FindLongestString(str string) string {
	var re = regexp.MustCompile("[a-zA-Z]+")
	var values = re.FindAll([]byte(str), -1)

	return string(findLongest(values))
}

// FindLongestInteger will retrieve a string like a SHA1, MD5 or whatever
// and return longest integer (first one)
func FindLongestInteger(str string) (int64, error) {
	var re = regexp.MustCompile("[0-9]+")
	var values = re.FindAll([]byte(str), -1)
	var max = findLongest(values)

	var a, err = strconv.Atoi(string(max))
	if err != nil {
		return 0, err
	}
	return int64(a), nil
}

func findLongest(values [][]byte) []byte {
	var max []byte
	for _, value := range values {
		if len(value) > len(max) {
			max = value
		}
	}
	return max
}
Tags: #golang

I published a new package today to retrieve the current price for a crypto currency from Coinbase.

I used this in another project and thought it will make sense to publish this as an own package. For this, you don’t need a Coinbase account or something.

You can find the package on GitHub and on pkg.go.dev.

Go Reference

Tags: #golang

I created a new package today to create a Payone request.

You can find the package on GitHub and on pkg.on.dev.^:s

Go Reference

Tags: #golang

I build a tool today to generate a HTML page from several RSS feeds. Inspired by engineeringblogs.xyz (GitHub).

go run Reader.go example-urls > example-output.html

The generated HTML page will lool like this. You can find the code on GitHub.

Tags: #golang

Implemented the old game “Hi-Lo” in Go today after I stumpled over this blog post and basic-computer-games.

This game is an adaptation of the game GUESS; however, instead of just guessing a number between 1 and 100, in this game you win dollars when you guess the number.

You can find my implementation in GitHub

Tags: #golang

Like some other people, I played Wordle the last days. Today, I wrote a little tool to find out whats can be a good opener. Jotto is a similar game so it’s easy to find a list of words like this https://www.easysurf.cc/list1.htm

I downloaded and checked it against my helper tool. The code is not a beauty but it works (and I hope correctly)

package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
)

func main() {
	checkAgainstWordlist()
}

func checkAgainstWordlist() {
	lettercount := make(map[string]int)

	file, err := os.Open("Wordlist")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {

		for _, letter := range scanner.Text() {
			if val, ok := lettercount[rune2c(letter)]; ok {
				lettercount[rune2c(letter)] = val + 1
			} else {
				lettercount[rune2c(letter)] = 1
			}
		}
	}
	fmt.Println(rankByWordCount(lettercount))

	if err := scanner.Err(); err != nil {
		panic(err)
	}
}

func rune2c(r rune) string {
	return fmt.Sprintf("%c", r)
}

// https://stackoverflow.com/questions/18695346/how-can-i-sort-a-mapstringint-by-its-values
func rankByWordCount(wordFrequencies map[string]int) PairList {
	pl := make(PairList, len(wordFrequencies))
	i := 0
	for k, v := range wordFrequencies {
		pl[i] = Pair{k, v}
		i++
	}
	sort.Sort(sort.Reverse(pl))
	return pl
}

type Pair struct {
	Key   string
	Value int
}

type PairList []Pair

func (p PairList) Len() int {
	return len(p)
}

func (p PairList) Less(i, j int) bool {
	return p[i].Value < p[j].Value
}

func (p PairList) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

The result was

[{e 709} {a 679} {r 565} {t 499} {i 480} {o 457} {s 429} {l 428} {n 418} {c 352} {u 335} {h 302} {d 258} {p 240} {y 238} {m 222} {g 203} {b 185} {k 154} {w 137} {f 137} {v 99} {x 30} {q 26} {z 24} {j 19}]

(e = 709 times, a = 679 times, …)

That means e,a,r,t, and i are the most used letters in the list. A word for this combination of letters can be IRATE.

Update 14.01.2022

I got the Wordlist from Wordle itself and run my code again. The result are:

[{s 6665} {e 6662} {a 5990} {o 4438} {r 4158} {i 3759} {l 3371} {t 3295} {n 2952} {u 2511} {d 2453} {y 2074} {c 2028} {p 2019} {m 1976} {h 1760} {g 1644} {b 1627} {k 1505} {f 1115} {w 1039} {v 694} {z 434} {j 291} {x 288} {q 112}]

That meas a possible good opener can be AROSE.

Update 15.01.2022

Someone told me that Wordle are using two list. The first list for the Wordle itself and the second list for possible words. I made the mistake and combined both lists in the last result.

I tried it again with the list for the game and the result is slightly different:

[{e 1233} {a 979} {r 899} {o 754} {t 729} {l 719} {i 671} {s 669} {n 575} {c 477} {u 467} {y 425} {d 393} {h 389} {p 367} {m 316} {g 311} {b 281} {f 230} {k 210} {w 195} {v 153} {z 40} {x 37} {q 29} {j 27}]

e,a,r,o, and t are the most used letters. I don’t know a word with this letters so I think I will stay with AROSE as an opener.

Tags: #golang

I wrote a little tool to merge several individual ssh configuration files to a single configuration.

ssh-merge-config ~/.ssh/config.d/ ~/.ssh/config

That’s it. The code is on GitHub https://github.com/mnlwldr/ssh-merge-config.

Tags: #golang

println() is a built-in function and looks useful to developers, because they lack dependencies. But there is a difference between fmt.Println() and println().

println() write to stderr instead of stdout and I never noticed that before, maybe because I didn’t used println so much in the past.

I discovered this because I wanted to compare the throughput of

for {
    fmt.Println("y")
}

and

for {    
    println("y")    
}        

by pipe this to pv -r. So I wondered why I got a lot of “y” in the println() variant.

"The println built-in function formats its arguments in an implementation-specific way and writes the result to standard error. Spaces are always added between arguments and a newline is appended. Println is useful for bootstrapping and debugging; it is not guaranteed to stay in the language."

https://pkg.go.dev/builtin#println

println in the Golang built-in documentation

Tags: #golang

I wrote a little tool in Golang to move files from directory ‘a’ to ‘b’ I had a task today where, among other things, I had to keep moving files from one folder to another. After doing it 5 times, I wanted to automate it with fsnotify/fsnotify.

func main() {
	srcDir := "path/to/a"
	destDir := "path/to/b"

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer watcher.Close()

	done := make(chan bool)
	go func() {
		for {

			event, ok := <-watcher.Events
			if !ok {
				return
			}
			if event.Op.String() == "CREATE" {
				log.Println("[event] ", event)
				newPath := destDir + filepath.Base(event.Name)
				err := os.Rename(event.Name, newPath)
				if err != nil {
					log.Fatal(err)
				}
			}
		}
	}()
	err = watcher.Add(srcDir)
	if err != nil {
		log.Fatal(err)
	}
	<-done
}
Tags: #golang

Built @zuzakistan's (@BBCBweaking) OWO with Golang. Thinking about an IRC plugin now.

Source: mnlwldr/OWO.

package owo

import (
	"math/rand"
	"strings"
	"time"
)

// Define a prefixes variable as a slice of strings
var prefixes = []string{
	"<3 ",
	"0w0 ",
	"H-hewwo?? ",
	"HIIII! ",
	"Haiiii! ",
	"Huohhhh. ",
	"OWO ",
	"OwO ",
	"UwU ",
}

// Define a suffixes variable as a slice of strings
var suffixes = []string{
	" ( ͡° ᴥ ͡°)",
	" (இωஇ )",
	" (๑•́ ₃ •̀๑)",
	" (• o •)",
	" (●´ω`●)",
	" (◠‿◠✿)",
	" (✿ ♡‿♡)",
	" ( \"◟ \")",
	" (人◕ω◕)",
	" (;ω;)",
	" (`へ´)",
	" ._.",
	" :3",
	" :D",
	" :P",
	" ;-;",
	" ;3",
	" ;_;",
	" >_<",
	" >_>",
	" UwU",
	" XDDD",
	" ^-^",
	" ^_^",
	" x3",
	" x3",
	" xD",
	" ÙωÙ",
	" ʕʘ‿ʘʔ",
	" ʕ•̫͡•ʔ",
	" ㅇㅅㅇ",
	", fwendo",
	"(^v^)",
}

// Translate the given text. You can set withPrefix and withSuffix
// to true or false, if you want them or not.
// It returns the translated text
func Translate(text string, withPrefix bool, withSuffix bool) string {

	// Define a concat variable as a slice of strings
	var concat []string

	// if withPrefix is set to true add it to the concat slice
	if withPrefix {
		concat = append(concat, prefixes[random(len(prefixes))])
	}

	// substitute characters and add the processed string to the concat slice
	concat = append(concat, substitute(text))

	// if withSuffix is set to true add it to the concat slice
	if withSuffix {
		concat = append(concat, suffixes[random(len(suffixes))])
	}

	// Join the concat slice and return it as a string
	return strings.Join(concat, " ")
}

// It replaces characters in the given text
// It returns the processed string
func substitute(text string) string {
	return strings.NewReplacer(
		"r", "w",
		"l", "w",
		"R", "W",
		"L", "W",
		"no", "nu",
		"has", "haz",
		"have", "haz",
		"you", "uu",
		"the ", "da ",
		"The ", "Da ").Replace(text)
}

// It returns a random number
func random(max int) int {
	return rand.New(rand.NewSource(time.Now().UnixNano())).Intn(max)
}
Tags: #golang

In the last time, I really interested in learn Golang. I like to learn while I code things and don’t read hundreds of blogs or books. So, here I show you, how I created a really simple IRC bot in Golang.

IRC

Long before Discord, Slack or something else, we had IRC, and the best thing is, it’s still here. If you want to read more about IRC, here is the Wikipedia article.

IRC is an application layer protocol that facilitates communication in the form of text. The chat process works on a client/server networking model.

Lets start

In this blog, I don’t explain everything. The most things are really easy to teach it to yourself from the official documentation.

How the most projects, I started with the Golang skeleton:

package main

func main() {

}

Connect

The first thing that comes to mind is to connect to the server. In Golang, you can simple import a package called `net`.
import (
    "net"
)

Package net provides a portable interface for network I/O, including TCP/IP, UDP, domain name resolution, and Unix domain sockets.

The Dial function connects to a server, for example: net.Dial("tcp", "your_server:your_port").

You can read more information about net and net.Dial here: golang.org/pkg/net/

Now I added a new function and name it … connect() … wow :D.

func connect() net.Conn {
    conn, err := net.Dial("tcp", "your_server:your_port")
    if err != nil {
        panic(err)
    }
    return conn
}

Maybe, I can try to explain a bit here

func connect() net.Conn {

That’s just a function, net.Conn is the return type.

conn, err := net.Dial("tcp", "your_server:your_port")

In Golang, we can return more than one values. So the first returned value is conn and the second returned value is err here.

if err != nil {
    panic(err)
}

Simple error “handling”. panic(err) abort and exit with a non-zero exit status.

When no error happened, we return the connection

return conn

Disconnect

Normally, when you connect to something you want also to disconnect. So I add another function and name it ... `disconnect()` ... wow, again :).
func disconnect(conn net.Conn) {
    conn.close()
}

I pass conn into the function here and call simple conn.Close(), to close the connection, obviously.

Now we can connect and disconnect to the server. Our code looks now like this:

package main

import (
	"net"
)

func connect() net.Conn {
	conn, err := net.Dial("tcp", "your_server:your_port")
	if err != nil {
		panic(err)
	}
	return conn
}

func disconnect(conn net.Conn) {
	conn.Close()
}

func main() {
    conn := connect()
    disconnect(conn)
}

Read server messages

Somewhere between connect() and disconnect() we need to receive and sent messages. Like a smalltalk with people you don’t like. To read messages, we import three new packages: fmt, bufio, and net/textproto.

Package fmt implements formatted I/O with functions analogous to C’s printf and scanf.

Package bufio implements buffered I/O.

Package textproto implements generic support for text-based request/response protocols in the style of HTTP, NNTP, and SMTP.

So we put all together and add the following code between connect() and disconnect() in our main() function.

tp := textproto.NewReader(bufio.NewReader(conn))

for {
    status, err := tp.ReadLine()
    if err != nil {
        panic(err)
    }
    fmt.Println(status)
}

for {} is like an endless loop, read every line from conn and print it out to our screen.
Now, we generate following output (I try to connect to Freenode in this example)

:hitchcock.freenode.net NOTICE * :*** Looking up your hostname...
:hitchcock.freenode.net NOTICE * :*** Checking Ident
:hitchcock.freenode.net NOTICE * :*** Couldn't look up your hostname
:hitchcock.freenode.net NOTICE * :*** No Ident response

So far so good, we can receive messages.

Talk with the server

After we can connect, read the server messages and disconnect, we need to “talk” with the server. In the real world, when we meet someone new we say our name … mostly. Same with our connection to the IRC server. The IRC protocol has two commands for this. USER and NICK.

So we need a function to say something like “Hi Server, I’m TheManWithTheIceCreamVan” or something else. We used already the package fmt to print stuff to the screen, but we can use fmt also, to send a message to the server. We need the function Fprintf to do this.

For example: fmt.Fprintf(os.Stdout, "%s is %d years old.\n", name, age)

The first parameter of Fprintf can be our conn variable, and the rest is just the string to “write”.

For example, we can create a function and calling it logon().

func logon(conn net.Conn) {
    fmt.Fprintf(conn, "USER TheManWithTheIceCreamVan 8 * :Someone\r\n")
    fmt.Fprintf(conn, "NICK TheManWithTheIceCreamVan\r\n")
}

At this point, we should directly create a helper function to send data to the server. We create a function and name it sendData().

func sendData(conn net.Conn, message string) {
    fmt.Fprintf(conn, "%s\r\n", message)
}

We simple pass the connection conn, and the message to send to this function, the function itself calling fmt.Fprintf then.

Back to our logon() method, we use this new created function.

func logon(conn net.Conn) {
    sendData(conn, "USER TheManWithTheIceCreamVan 8 * :Someone")
    sendData(conn, "NICK TheManWithTheIceCreamVan")
}

After the connect() line in our main() function, we can call login(conn) now. Our code looks now like this:

package main

import (
	"net"
	"fmt"
	"net/textproto"
	"bufio"
)

func connect() net.Conn {
	conn, err := net.Dial("tcp", "irc.freenode.net:6667")
	if err != nil {
		panic(err)
	}
	return conn
}

func disconnect(conn net.Conn) {
	conn.Close()
}

func logon(conn net.Conn) {
    sendData(conn, "USER TheManWithTheIceCreamVan 8 * :Someone")
    sendData(conn, "NICK TheManWithTheIceCreamVan")
}

func main() {
    conn := connect()
	 logon(conn)

	 tp := textproto.NewReader(bufio.NewReader(conn))

	for {
		status, err := tp.ReadLine()
		if err != nil {
			panic(err)
		}
		fmt.Println(status)
	}

    disconnect(conn)
}

func sendData(conn net.Conn, message string) {
    fmt.Fprintf(conn, "%s\r\n", message)
}

Now we got a lot of messages (MOTD, Stats, etc.) from the Freenode Server.

:tolkien.freenode.net NOTICE * :*** Looking up your hostname...
:tolkien.freenode.net NOTICE * :*** Checking Ident
:tolkien.freenode.net NOTICE * :*** No Ident response
:tolkien.freenode.net NOTICE * :*** Couldn't look up your hostname
:tolkien.freenode.net 001 TheManWithTheIce :Welcome to the freenode Internet Relay Chat Network TheManWithTheIce
:tolkien.freenode.net 002 TheManWithTheIce :Your host is tolkien.freenode.net[204.225.96.251/6667], running version ircd-seven-1.1.9
:tolkien.freenode.net 003 TheManWithTheIce :This server was created Fri Apr 24 2020 at 22:19:21 UTC

...

:tolkien.freenode.net 376 TheManWithTheIce :End of /MOTD command.
:TheManWithTheIce MODE TheManWithTheIce :+i

The last line means, we got USERMODE +i from the IRC server. That’s because we requested this with the “8” in our USER command:

sendData(conn, "USER TheManWithTheIceCreamVan 8 * :Someone")

Our Bot is connected to the server. When you /whois TheManWithTheIceCreamVan from your regular IRC client you should get something like this

21:04 -!- TheManWithTheIce [~TheManWit@1.1.1.1]
21:04 -!-  ircname  : Someone
21:04 -!-  server   : hitchcock.freenode.net [Sofia, BG, EU]
21:04 -!- End of WHOIS

PONG

At regular intervals, the IRC server send you a “PING” message and when you don’t answer this message with a “PONG” message, the server will close the connection after a few seconds.

:TheManWithTheIce MODE TheManWithTheIce :+i
PING :hitchcock.freenode.net
:TheManWithTheIce!~TheManWit@37.228.165.230 QUIT :Ping timeout: 258 seconds

All we need is to check if we receive a message that start with “PING”. There’re different possibilities to do this, some people use regular expressions for this, we just use HasPrefix() function from the strings packages. For example:

if strings.HasPrefix("FOOBAR", "FOO") {
    // FOOBAR start with FOO
} else {
    // FOOBAR don't start with FOO
}

First, we could create a new method and name it … pong() :)

func pong(conn net.Conn) {
	sendData(conn, "PONG")
}

It’s just like the logon() function. Everything we need to do is to send a simple “PONG” as message to the server. Now we can check the incoming messages and answer with our pong() function:

func main() {
    conn := connect()
    logon(conn)

    tp := textproto.NewReader(bufio.NewReader(conn))

    for {
        status, err := tp.ReadLine()
        if err != nil {
            panic(err)
        }

        fmt.Println(status)

        if strings.HasPrefix(status, "PING") {
            pong(conn)
        }
    }

    disconnect(conn)
}

SIGTERM

You kill the bot mostly with an SIGTERM, like CTRL+C. To catch this and give the bot the possibility to for a clean `disconnect()` we can implement the following:
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
    <-c
    disconnect(conn)
}()

… and add this to our main() function

func main() {
    conn := connect()

    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    go func() {
        <-c
        disconnect(conn)
    }()

    logon(conn)

    tp := textproto.NewReader(bufio.NewReader(conn))

    for {
        status, err := tp.ReadLine()
        if err != nil {
            panic(err)
        }

        fmt.Println(status)

        if strings.HasPrefix(status, "PING") {
            pong(conn)
        }
    }

    disconnect(conn)
}

QUIT

It’s a good manner to say goodbye politely. So before we hard disconnect() from the server, we send the QUIT command

func disconnect(conn net.Conn) {
    sendData(conn, "QUIT Bye")
    conn.Close()
}

Full code

package main

import (
	"bufio"
	"fmt"
	"net"
	"net/textproto"
	"os"
	"os/signal"
	"strings"
	"syscall"
)

func connect() net.Conn {
	conn, err := net.Dial("tcp", "irc.freenode.net:6667")
	if err != nil {
		panic(err)
	}
	return conn
}

func disconnect(conn net.Conn) {
	sendData(conn, "QUIT Bye")
	conn.Close()
}

func login(conn net.Conn) {
	sendData(conn, "USER TheManWithTheIceCreamVan 8 * :Someone")
	sendData(conn, "NICK TheManWithTheIceCreamVan")
}

func pong(conn net.Conn) {
	sendData(conn, "PONG")
}

func main() {

	conn := connect()

	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		disconnect(conn)
	}()

	login(conn)

	tp := textproto.NewReader(bufio.NewReader(conn))

	for {
		status, err := tp.ReadLine()
		if err != nil {
			panic(err)
		}

		fmt.Println(status)

		if strings.HasPrefix(status, "PING") {
			pong(conn)
		}
	}
}

func sendData(conn net.Conn, message string) {
	fmt.Fprintf(conn, "%s\r\n", message)
}

Maybe, I will update this post sometime to show, how to join a channel and “talk” with other people :)

Tags: #golang