Skip to content

Commit 5bd6962

Browse files
cpanatoalexellis
authored andcommitted
Blog: using openfaas to run a twitter bot
Feedback and editing by Alex Signed-off-by: Carlos Panato <[email protected]> Signed-off-by: Alex Ellis (OpenFaaS Ltd) <[email protected]>
1 parent 6a49b07 commit 5bd6962

File tree

5 files changed

+227
-0
lines changed

5 files changed

+227
-0
lines changed
+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
---
2+
title: "I wrote a Twitter Bot using OpenFaaS to avoid missing out on CfP deadlines"
3+
description: "I wrote a Twitter bot using Golang and OpenFaaS so that I wouldn't miss the deadline for CNCF talk submissions or Kubernetes releases"
4+
date: 2021-06-07
5+
image: /images/2021-06-cloudnativetv/background-twitter.png
6+
categories:
7+
- twitter
8+
- bot
9+
- golang
10+
- usecase
11+
- functions
12+
author_staff_member: cpanato
13+
dark_background: true
14+
15+
---
16+
17+
I wrote a Twitter bot using Golang and OpenFaaS so that I wouldn't miss the deadline for CNCF talk submissions or Kubernetes releases.
18+
19+
In this blog post, I'll show you how I put the bot together and what tools I used along the way so that you can build your own and get ideas for your own projects.
20+
21+
## Introduction
22+
23+
Bots are everywhere. Some are useful that can help you in your daily work, and others are collecting information about you to use for ads.
24+
25+
> I think that the best bots are both fun and useful.
26+
27+
I created [@CloudNativeBot](https://twitter.com/CloudNativeBot) because: I would like to learn a bit more about OpenFaaS and the ecosystem around that.
28+
29+
I wanted to:
30+
* let people know when the next KubeCon was coming up and when the Call For Papers (CFP) deadline was
31+
* to show when the [Kubernetes](https://kubernetes.io/) Patch Releases land
32+
* and to bring some joy
33+
34+
Most importantly, I want you to have fun, discover new technologies that can be applied to your daily work.
35+
36+
## What you'll need to try it out
37+
38+
Before you get started you'll need Docker on your machine so that you can deploy a Kubernetes cluster and build OpenFaaS functions.
39+
* [Docker or Docker Desktop](https://www.docker.com/products/docker-desktop)
40+
41+
Then you'll need the following, which you can get by following the docs:
42+
43+
* [OpenFaaS](https://www.openfaas.com) - to run the functions
44+
* [faas-cli](https://github.com/openfaas/faas-cli) - the CLI for OpenFaaS
45+
* [connector-sdk](https://github.com/openfaas/connector-sdk) - the SDK for writing event connectors to trigger functions, in this instance from Twitter
46+
* [ko](https://github.com/google/ko) - a project to hot-reload our Twitter connector in the cluster as we iterate on it
47+
48+
### Using Connector-sdk to listen and dispatch the Twitter Stream
49+
50+
The connector-sdk helps you to connect events to functions. In the beginning of the code you bind to your streaming data source like Twitter, NATS or a cron timer, then whenever a message is received, the topic or channel name is used to trigger matching functions.
51+
52+
You can learn more here: [OpenFaaS Triggers](https://docs.openfaas.com/reference/triggers/)
53+
54+
![Connector pattern](https://docs.openfaas.com/images/connector-pattern.png)
55+
> Functions subscribe to events by adding a "topic" annotation at deploy time
56+
57+
I was able to setup my connector with just a few lines of code. In the case of Twitter, we need to configure the SDK to listen a stream for various messages like `/cloudnativetv` or `/k8s-patch-schedule`.
58+
59+
Here's an example of how to connect the stream using the Twitter SDK and [anaconda library](https://github.com/ChimeraCoder/anaconda).
60+
61+
```golang
62+
api = anaconda.NewTwitterApiWithCredentials(Config.TwitterAccessToken, Config.TwitterAccessSecret, Config.TwitterConsumerKey, Config.TwitterConsumerSecretKey)
63+
if _, err := api.VerifyCredentials(); err != nil {
64+
log.Fatalf("Bad Authorization Tokens. Please refer to https://apps.twitter.com/ for your Access Tokens: %s", err)
65+
}
66+
67+
streamValues := url.Values{}
68+
streamValues.Set("track", "/cloudnativetv,/k8s-patch-schedule,/kubecon-random-video")
69+
streamValues.Set("stall_warnings", "true")
70+
log.Println("Starting CloudNative Stream...")
71+
s := api.PublicStreamFilter(streamValues)
72+
```
73+
74+
After that, we need to configure the connector-sdk with the password for the gateway and any other configuration we want.
75+
76+
If you expect to receive many messages over a short period of time, or concurrent slow-running functions then you can set `AsyncFunctionInvocation` to true for execution in the background.
77+
78+
```golang
79+
creds := &auth.BasicAuthCredentials{
80+
User: Config.OpenFaaSUsername,
81+
Password: Config.OpenFaaSPassword,
82+
}
83+
84+
config := &types.ControllerConfig{
85+
RebuildInterval: time.Millisecond * 1000,
86+
GatewayURL: Config.OpenFaaSGateway,
87+
PrintResponse: true,
88+
PrintResponseBody: true,
89+
AsyncFunctionInvocation: false,
90+
}
91+
92+
controller := types.NewController(creds, config)
93+
94+
receiver := ResponseReceiver{}
95+
controller.Subscribe(&receiver)
96+
97+
controller.BeginMapBuilder()
98+
```
99+
100+
Then we can create an event-loop in a separate Go routine to listen to the events and dispatch them when required.
101+
102+
The `controller.Invoke()` function takes a topic and a number of bytes. So the topic could be `"cloudnative.twitter.stream"` and the message in this instance will be a JSON payload that can be parsed by the target function.
103+
104+
```golang
105+
go func() {
106+
for t := range s.C {
107+
switch v := t.(type) {
108+
case anaconda.Tweet:
109+
data := []byte(fmt.Sprintf(`{"text": %q,"id": %d, "id_str": %q, "user_screen_name": %q, "api_key": %q}`, v.Text, v.Id, v.IdStr, v.User.ScreenName, Config.TokenAPIKey))
110+
111+
topic := "cloudnative.twitter.stream"
112+
log.Printf("Got one message - https://twitter.com/%s/status/%d - Invoking on topic %s\n", v.User.ScreenName, v.Id, topic)
113+
controller.Invoke(topic, &data)
114+
default:
115+
log.Printf("Got something else %v", v)
116+
}
117+
}
118+
}()
119+
```
120+
121+
The complete source code can see here: https://github.com/cpanato/cloudnative-bot/blob/main/cmd/twitter-stream/main.go.
122+
123+
Now we are in part to deploy the service into a Kubernetes Cluster.
124+
125+
All other connectors have a Dockerfile and help chart, but I decided to use the [`ko`](https://github.com/google/ko) tool for building my connector. `ko` simplifies and makes it easier to create a container image for your Go application without having to write a Dockerfile.
126+
127+
> Note: The community connectors do however use a Dockerfile and it is easy to use as a template for your own. For example, the [nats-connector](https://github.com/openfaas/nats-connector).
128+
129+
One way to use `ko` is to define your deployment manifest, and in the image field, have the `ko` syntax, for example:
130+
131+
```yaml
132+
apiVersion: apps/v1
133+
kind: Deployment
134+
metadata:
135+
name: cloudnative-bot-stream
136+
labels:
137+
app.kubernetes.io/name: cloudnative-bot-stream
138+
spec:
139+
replicas: 1
140+
spec:
141+
containers:
142+
- name: stream
143+
image: ko://github.com/cpanato/cloudnative-bot/cmd/twitter-stream
144+
imagePullPolicy: IfNotPresent
145+
```
146+
Note: some lines were removed from the manifest to make it more clear.
147+
148+
After defining the deployment, you can run `ko` and the output will be the updated manifests ready to be deployed and the image pushed to the registry.
149+
150+
```console
151+
$ export KO_DOCKER_REPO=YOUR_REGISTRY_NAME
152+
$ ko resolve -f deployment.yaml > release.yaml
153+
```
154+
155+
### Using OpenFaaS functions for the slash commands
156+
157+
We are using OpenFaaS to deploy the slash command functions for the bot, and those will be triggered by the connector-sdk code we show above.
158+
159+
The `CloudNativeBot` have currently three functions:
160+
161+
* `/cloudnativetv` - returns a random logo for the CNCF CloudNative TV shows
162+
* `/k8s-patch-schedule` - returns the patch schedule for the active release branches fro Kubernetes
163+
* `/kubecon-random-video`- returns a random KubeCon video
164+
165+
The code for each function you can found in the [cloudnative-bot](https://github.com/cpanato/cloudnative-bot) repo.
166+
167+
An example of how that works:
168+
169+
* A Twitter user post a message `/kubecon-random-video`
170+
* The stream listener will catch that and will trigger the function
171+
* The specific function, in this case, the `kubecon-random-video` will grab a random video from Youtube and post a reply message on Twitter.
172+
173+
![https://twitter.com/comedordexis/status/1398286724569182219](/images/2021-06-cloudnativetv/twitter-bot-example.png)
174+
> Example response with a random video from the last KubeCon event.
175+
176+
### Running functions running on a schedule
177+
178+
Now we've covered the functions which are triggered by user-input. We still need to talk about the function that reminds us about the upcoming deadline for talk submissions. You can't give a talk at a conference, if you've not submitted a talk abstract and sometimes these dates come up so quickly, so I wanted to make sure I never missed one.
179+
180+
These functions are similar to the ones we created above, but instead of being triggered by the Twitter connector I built and deployed with ko, they are triggered by the [`cron-connector`](https://github.com/openfaas/cron-connector).
181+
182+
The `cron-connector` is an add-on that you can install along side your OpenFaaS deployment and then using some annotations in your function you can make it run using a defined schedule.
183+
184+
To define the schedule for your function you can add the following in your `stack.yaml`
185+
186+
```yaml
187+
functions:
188+
remind-cfp:
189+
image: ghcr.io/cpanato/remind-cfp:1.0.0
190+
annotations:
191+
topic: cron-function
192+
schedule: "*/5 * * * *"
193+
```
194+
195+
The `topic` is always set to `cron-function` and the `schedule` is a standard Cron expression.
196+
197+
For [faasd](https://github.com/openfaas/faasd) users, you can use cron on your system or deploy the cron-connector. faasd can run on a single virtual machine or Raspberry Pi without the overhead of Kubernetes.
198+
199+
### Summing up
200+
201+
With OpenFaaS and the connector-sdk I was able to bootstrap my project quickly and iterate on it until I had everything in place.
202+
203+
Feel free to test it out, the commands are:
204+
205+
* `/cloudnativetv`
206+
* `/k8s-patch-schedule`
207+
* `/kubecon-random-video`
208+
209+
If you like, feel free to fork the repo and build your own bot. The functions can be written in any language that you can find in the template store via `faas-cli template store list` or you can just customise mine to suit your needs.
210+
211+
You can even build your own Slack and GitHub bots with OpenFaaS. Find out what the community and customers are doing in: [Exploring Serverless Use-cases from Companies and the Community](https://www.openfaas.com/blog/exploring-serverless-live/)
212+
213+
What to do next:
214+
215+
- Follow the [@CloudNativeBot](https://twitter.com/CloudNativeBot)
216+
- Read this blog post on [Golang and OpenFaaS](https://www.openfaas.com/blog/golang-serverless/)
217+
- Discover more tools and functions in the OpenFaaS ecosystem: https://github.com/openfaas
218+
- Checkout [KubeCon US](https://events.linuxfoundation.org/kubecon-cloudnativecon-north-america/) - October 2021

_staff_members/cpanato.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
name: Carlos Tadeu Panato Junior
3+
position: Contributor
4+
image_path: /images/author/cpanato.png
5+
linkedin_username: carlospanato
6+
github_username: cpanato
7+
twitter_username: comedordexis
8+
blurb: Opensource contributor to several projects.
9+
---
Loading
Loading

images/author/cpanato.png

232 KB
Loading

0 commit comments

Comments
 (0)