Devember - From CLI to GUI

Hello everyone,

This is my first devember / proper programming challenge ever, this will be interesting!

Goal
The first proper programming project of size that I have ever completed is HappyBot, a feature-rich CLI Twitch bot written in Golang. It’s stable (from my testing), fast, and leaves a low foot print.

The thing is, there is a very small number of people who would even consider using a CLI for their bot. Thus, my main goal is to write a front-end web GUI for the bot. I have not ever written a proper GUI in my life, so I have no idea how to approach this.

Limitations in the First Week
As I write this on November 30th, I am a few days away from finals week. Thus, this first week from me will be pretty light. I plan on using this first week to determine what I want to use for the GUI (I am super open to suggestions), and tightening up a copy pasta protection feature that I am working on.

Measure of Success
I do not expect to have the GUI 100% complete at the end of Devember. My main objective here is to have the technology for the GUI picked out, and to have a GUI working with some basic features of the bot. Such as starting it, maybe a window that outputs the log? It seems small, but honestly writing a GUI terrifies me.

4 Likes

Good luck! :slightly_smiling_face:
You might want to put an entry into the participants list!

1 Like

Done, thanks for the heads up!

1 Like

I like the project! Making software more accessible is always a good thing. Also, nice documentation on GitHub.

Never mind that I don’t use twitch :wink:

Day 1

Hey! Wanted to give my first official update! As I say in the first post, this first week will be light due to finals, but got a few things done today.

Non-GUI Stuff
I pushed to the repo an early implementation of the copy pasta / spam protection system. It consists of a hashmap that stores (when enabled through the config file) every message that is sent through to the chat. It stores the message as a key, then with a triple value pair, stores: # of times posted in chat, the time the original post was made, and when the last post was.

Users can set in the config.toml a few arguments to customize this.

GUI Stuff
Likely during finals I will be in a planning phase for what / how to implement the gui.

Here’s what I don’t know how to do yet:

  • Write an API with the back-end golang code that can be exposed to the GUI
  • The technology for the GUI. My early thoughts are: using a framework such as React, OR trying to figure what the heck this Polymer thing is.

I’ve maybe written 10 lines of JavaScript in my life, so I’m not sure where to go with that yet.

1 Like

Day 2

This will be a short one, and I may not be able to post again until Tuesday or Wednesday.

Frustrations
Today was a little frustrating. Partially because I am appreciating now how heavy the move to CLI to GUI might be.

The problem is that the bot, as it stands, is around 1500 lines of code. I have to figure out how to make an API, with all of that code already written and stable, and exposing it without breaking it.

The implementation might not be that complciated, but I sure can’t wrap my head around the design of it right now.

Next Steps

I do think a framework solution such as React is the way to go. It’s a useful skill to have, as much as I hate the footprint that big ‘electron-y’ applications leave on system resources.

  1. I think the first thing to try is to get some data from the bot’s local SQLite3 database and get it to load dynamically on a web page.
  2. Luckily, Twitch makes it easy to embed the Twitch chat (similar in embedding a Youtube video) to any other page, so that I don’t have to write a chat window and people can see the emotes instead of a text-only chat.
  3. Something that would be very useful would be to make it so that users can change their config.toml settings from the web front-end. How am I gonna make that work? No clue, lol.

Anyway, gotta focus on the final now, hopefully once I return I will be clear-headed and more patient.

React !== Electron.

React in production mode only add’s like a 1/2 a megabyte to the bundle size.

Depending on the use of SVG’s or font ligatures, they might be the main source of the increased asset size.

The size of Electron stuff is because it comes bundled with a stripped down version of Chromium, while you write the front end GUI in HTML5/CSS/JS and can interface with the local system via an API.

Still, though that’s only like ~55 megabytes.

Honestly, there’s no reason for an electron app to exist so long as your app is a properly implemented PWA (progressive web app). Unless of course it is highly specific, like a source code editor like VSCode.

Ever wonder why there’s no L1 app? Because there’s no need. This site is a properly implemented PWA. If you send it to the desktop on a phone you’ll see what I’m talking about.


One approach you might want to look into is seeing if your Go program can interface with Python or vice versa. Because then you could bootstrap a Flask server in like 5 lines of code and write a simple GUI with HTML and CSS.

Or you could take my approach and use Mirthril with webpack xD

shameless devember plug

Good luck! :shamrock:

1 Like

Depends on what you want to achieve really. For a forum. Sure, I don´t need an app.

If the browser where the awnswer to everything UI/UX wise, we´d all be using a chromebook by now, however.

Say, you brought up VSCode. It´s writte in electron, with javascript/typescript. I´d very much be annoyed by it, if it where stuck in a browser tab.

And with actual bare web apps like this forum. You have to host a server somewhere and if that server goes away, the app does too. Sounds like a bad idea to me for a tiny twitch bot.

Finally done with finals and can get back to this when not working.

I looked up Mirthril, and it actually looks nice for my use case. A small JS framework for single-page apps (which is what mine will be) sounds perfect.

What’s the benefit of using webpack with Mirthril?

1 Like

Even better asset management. Take a look at the benefits of webpack. Webpack can be used for any application, but absolutely needed if high performance is priority.

https://webpack.js.org/

If you need help, you can look at my git repo for my devember thread, I have a pretty good webpack config that I spent a few hours tweaking.

Day 3 to 7

Hey everyone, as I’ve said before, this week I had to focus on finals. But I was finally able to get back into the routine of things today.

What Happened

A good part of today was exploring the high-level of implementing an API for HappyBot and what to do for the front-end.

Actually, that part will be easier than I initially thought. I already have a SQLite3 database that stores a lot of data needed for the bot to run (commands, bannable words etc), and golang functions to return that data. So all I’ve had to do today was figure out how to write handlers that got that data and encoded them to JSON to an endpoint.

Thanks to the suggestion from @Dynamic_Gravity, I’m exploring using Mithril JS for the front-end. I’ve messed with it a bit, but have not gotten it to get data from the API yet. My JavaScript foo is non-existent, so it might take me a bit.

Tomorrow

If all I accomplish tomorrow is getting the JS app to get raw data from the API, I’ll be happy. If I can do that quick, I will start add some gui elements such as buttons to make stuff happen.

1 Like

Here’s an example of an api fetch.

models/Users.js

const m = require('mithril')

let Users = {
  list: [],
  loadList: async () => {
    const result = await m.request({
      method: 'GET',
      url: 'https://rem-rest-api.herokuapp.com/api/users',
      withCredentials: true
    })
    Users.list = result.data
  }
}

module.exports = Users

views/UserList.js

const m = require('mithril')
const Users = require('../models/Users')

const UserList = {
  oninit: Users.loadList,
  view: () => {
    return m('ul', Users.list.map(u => {
      return m('li', u.firstName + ' ' + u.lastName)
    }))
  }
}

module.exports = UserList

Thanks for the info! I’ve seen the Users.js example somewhere but not UserList.js.

Is the point of UserList.js to get the information from JSON into a map that JavaScript can use?

The API fetch happens when the UserList model is initialized. It does that by calling the loadList function that is defined in the User module.

The point of UserList is to conform to the MVC methodology (Model View Controller).

What we’re telling it to do is to create a new array based on the data inside User.list component which we got from the JSON data.

It’s a bit complicated in full detail, but in essence, a response body is pure JSON so most of the time when you see arrays they will contain this data type.

Once we have the data, when the promise resolves, we map it out and create new list elements.

This just made me realize i need to refactor Userlist into a controller directory instead of a view one.

Day 8

Sadly this was a bit of a frustrating day.

So I began by having some issues not getting any JSON at all from my Golang API endpoint. Turns out, the url I was using in JS was ‘localhost:3000/api’ where I had to use ‘http://localhost:3000/api’, took a little too long to figure that out.

Decided to mess with some other frameworks and see if the issues continued, and they did, and really did not like the bloat (especially from Angular) as compared to Mithril.

So: bascially, I’m to the point where I can use a stupid simple program from Mithril’s API that will console.log the raw data from my API, but as soon as I try to print it out in HTML for later use, I get absolutely nothing.

So yeah, frustrated, gonna keep at it tomorrow though.

Day 9

This day started with my general feeling from day 8: frustrated about being able to show my API data with console.log but unsuccessful with displaying it on the page.

Turns out, it had nothing to do with the javascript, it was the Golang.

I have a function that queries my local SQLite3 database that retrieves all of the twitch chat commands that a user has for his/her stream. I then just took that data, let Gin serialize it into JSON, and the JavaScript would display it.

Problem was that this data was in the form of a key value hashmap making each json ‘entry’ a different key name which messed it up.

Anyways, it’s fixed, and stuff is displaying now. WOO!

Next Steps

I suppose the next step would be to get some basic GUI elements working. Do you have much experience with it @Dynamic_Gravity if you don’t mind me asking?

1 Like

Yeah, depends on what you use to display the data though. Could you post a sample of the expected data payload?

1 Like

I can get you the actual JSON if it’s helpful when I’m back at home, but here’s an example.

Users of the bot can create commands. For example if they create a command “!twitter”, the bot will respond with text.

Command | Response
!twitter -------- ‘Check out my twitter! twitter.com/user

Thus, the payload will almost always be text values from a SQLIte3 database that will be serialized to JSON such as: ‘commandname: !twitter commandresponse: blah blah blah’.

Occasionally there will be integer values for the points system, but it is all similar to the example.