day 13
29
Days/Go/day13_example1.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
const DaysTotal int = 90
|
||||||
|
var remainingDays uint = 90
|
||||||
|
challenge := "#90DaysOfDevOps"
|
||||||
|
|
||||||
|
fmt.Printf("Welcome to the %v challenge.\nThis challenge consists of %v days\n", challenge, DaysTotal)
|
||||||
|
|
||||||
|
var TwitterName string
|
||||||
|
var DaysCompleted uint
|
||||||
|
|
||||||
|
// asking for user input
|
||||||
|
fmt.Println("Enter Your Twitter Handle: ")
|
||||||
|
fmt.Scanln(&TwitterName)
|
||||||
|
|
||||||
|
fmt.Println("How many days have you completed?: ")
|
||||||
|
fmt.Scanln(&DaysCompleted)
|
||||||
|
|
||||||
|
// calculate remaining days
|
||||||
|
remainingDays = remainingDays - DaysCompleted
|
||||||
|
|
||||||
|
fmt.Printf("Thank you %v for taking part and completing %v days.\n", TwitterName, DaysCompleted)
|
||||||
|
fmt.Printf("You have %v days remaining for the %v challenge\n", remainingDays, challenge)
|
||||||
|
fmt.Println("Good luck")
|
||||||
|
}
|
74
Days/Go/day13_example2.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// other imports
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/dghubble/go-twitter/twitter"
|
||||||
|
"github.com/dghubble/oauth1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Credentials stores all of our access/consumer tokens
|
||||||
|
// and secret keys needed for authentication against
|
||||||
|
// the twitter REST API.
|
||||||
|
type Credentials struct {
|
||||||
|
ConsumerKey string
|
||||||
|
ConsumerSecret string
|
||||||
|
AccessToken string
|
||||||
|
AccessTokenSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClient is a helper function that will return a twitter client
|
||||||
|
// that we can subsequently use to send tweets, or to stream new tweets
|
||||||
|
// this will take in a pointer to a Credential struct which will contain
|
||||||
|
// everything needed to authenticate and return a pointer to a twitter Client
|
||||||
|
// or an error
|
||||||
|
func getClient(creds *Credentials) (*twitter.Client, error) {
|
||||||
|
// Pass in your consumer key (API Key) and your Consumer Secret (API Secret)
|
||||||
|
config := oauth1.NewConfig(creds.ConsumerKey, creds.ConsumerSecret)
|
||||||
|
// Pass in your Access Token and your Access Token Secret
|
||||||
|
token := oauth1.NewToken(creds.AccessToken, creds.AccessTokenSecret)
|
||||||
|
|
||||||
|
httpClient := config.Client(oauth1.NoContext, token)
|
||||||
|
client := twitter.NewClient(httpClient)
|
||||||
|
|
||||||
|
// Verify Credentials
|
||||||
|
verifyParams := &twitter.AccountVerifyParams{
|
||||||
|
SkipStatus: twitter.Bool(true),
|
||||||
|
IncludeEmail: twitter.Bool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can retrieve the user and verify if the credentials
|
||||||
|
// we have used successfully allow us to log in!
|
||||||
|
user, _, err := client.Accounts.VerifyCredentials(verifyParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("User's ACCOUNT:\n%+v\n", user)
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Go-Twitter Bot v0.01")
|
||||||
|
creds := Credentials{
|
||||||
|
AccessToken: os.Getenv("ACCESS_TOKEN"),
|
||||||
|
AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
|
||||||
|
ConsumerKey: os.Getenv("CONSUMER_KEY"),
|
||||||
|
ConsumerSecret: os.Getenv("CONSUMER_SECRET"),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := getClient(&creds)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error getting Twitter Client")
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tweet, resp, err := client.Statuses.Update("A Test Tweet from the future, testing a #90DaysOfDevOps Program that tweets, tweet tweet", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
log.Printf("%+v\n", resp)
|
||||||
|
log.Printf("%+v\n", tweet)
|
||||||
|
}
|
99
Days/Go/day13_example3.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// other imports
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/dghubble/go-twitter/twitter"
|
||||||
|
"github.com/dghubble/oauth1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Credentials stores all of our access/consumer tokens
|
||||||
|
// and secret keys needed for authentication against
|
||||||
|
// the twitter REST API.
|
||||||
|
type Credentials struct {
|
||||||
|
ConsumerKey string
|
||||||
|
ConsumerSecret string
|
||||||
|
AccessToken string
|
||||||
|
AccessTokenSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClient is a helper function that will return a twitter client
|
||||||
|
// that we can subsequently use to send tweets, or to stream new tweets
|
||||||
|
// this will take in a pointer to a Credential struct which will contain
|
||||||
|
// everything needed to authenticate and return a pointer to a twitter Client
|
||||||
|
// or an error
|
||||||
|
func getClient(creds *Credentials) (*twitter.Client, error) {
|
||||||
|
// Pass in your consumer key (API Key) and your Consumer Secret (API Secret)
|
||||||
|
config := oauth1.NewConfig(creds.ConsumerKey, creds.ConsumerSecret)
|
||||||
|
// Pass in your Access Token and your Access Token Secret
|
||||||
|
token := oauth1.NewToken(creds.AccessToken, creds.AccessTokenSecret)
|
||||||
|
|
||||||
|
httpClient := config.Client(oauth1.NoContext, token)
|
||||||
|
client := twitter.NewClient(httpClient)
|
||||||
|
|
||||||
|
// Verify Credentials
|
||||||
|
verifyParams := &twitter.AccountVerifyParams{
|
||||||
|
SkipStatus: twitter.Bool(true),
|
||||||
|
IncludeEmail: twitter.Bool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can retrieve the user and verify if the credentials
|
||||||
|
// we have used successfully allow us to log in!
|
||||||
|
user, _, err := client.Accounts.VerifyCredentials(verifyParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("User's ACCOUNT:\n%+v\n", user)
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
creds := Credentials{
|
||||||
|
AccessToken: os.Getenv("ACCESS_TOKEN"),
|
||||||
|
AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
|
||||||
|
ConsumerKey: os.Getenv("CONSUMER_KEY"),
|
||||||
|
ConsumerSecret: os.Getenv("CONSUMER_SECRET"),
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const DaysTotal int = 90
|
||||||
|
var remainingDays uint = 90
|
||||||
|
challenge := "#90DaysOfDevOps"
|
||||||
|
|
||||||
|
fmt.Printf("Welcome to the %v challenge.\nThis challenge consists of %v days\n", challenge, DaysTotal)
|
||||||
|
|
||||||
|
var TwitterName string
|
||||||
|
var DaysCompleted uint
|
||||||
|
|
||||||
|
// asking for user input
|
||||||
|
fmt.Println("Enter Your Twitter Handle: ")
|
||||||
|
fmt.Scanln(&TwitterName)
|
||||||
|
|
||||||
|
fmt.Println("How many days have you completed?: ")
|
||||||
|
fmt.Scanln(&DaysCompleted)
|
||||||
|
|
||||||
|
// calculate remaining days
|
||||||
|
remainingDays = remainingDays - DaysCompleted
|
||||||
|
|
||||||
|
//fmt.Printf("Thank you %v for taking part and completing %v days.\n", TwitterName, DaysCompleted)
|
||||||
|
//fmt.Printf("You have %v days remaining for the %v challenge\n", remainingDays, challenge)
|
||||||
|
// fmt.Println("Good luck")
|
||||||
|
|
||||||
|
client, err := getClient(&creds)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error getting Twitter Client, this is expected if you did not supply your Twitter API tokens")
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf("Hey I am %v I have been doing the %v for %v days and I have %v Days left", TwitterName, challenge, DaysCompleted, remainingDays)
|
||||||
|
tweet, resp, err := client.Statuses.Update(message, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
log.Printf("%+v\n", resp)
|
||||||
|
log.Printf("%+v\n", tweet)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
19
Days/Go/makefile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
BINARY_NAME=90DaysOfDevOps
|
||||||
|
|
||||||
|
build:
|
||||||
|
GOARCH=amd64 GOOS=darwin go build -o ${BINARY_NAME}_0.2_darwin main.go
|
||||||
|
GOARCH=amd64 GOOS=linux go build -o ${BINARY_NAME}_0.2_linux main.go
|
||||||
|
GOARCH=amd64 GOOS=windows go build -o ${BINARY_NAME}_0.2_windows main.go
|
||||||
|
GOARCH=arm64 GOOS=linux go build -o ${BINARY_NAME}_0.2_linux_arm64 main.go
|
||||||
|
GOARCH=arm64 GOOS=darwin go build -o ${BINARY_NAME}_0.2_darwin_arm64 main.go
|
||||||
|
|
||||||
|
run:
|
||||||
|
./${BINARY_NAME}
|
||||||
|
|
||||||
|
build_and_run: build run
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean
|
||||||
|
rm ${BINARY_NAME}-darwin
|
||||||
|
rm ${BINARY_NAME}-linux
|
||||||
|
rm ${BINARY_NAME}-windows
|
BIN
Days/Images/Day13_Go1.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
Days/Images/Day13_Go2.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
Days/Images/Day13_Go3.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
Days/Images/Day13_Go4.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
Days/Images/Day13_Go5.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
Days/Images/Day13_Go6.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
Days/Images/Day13_Go7.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
Days/Images/Day13_Go8.png
Normal file
After Width: | Height: | Size: 146 KiB |
BIN
Days/Images/Day13_Go9.png
Normal file
After Width: | Height: | Size: 17 KiB |
@ -1,19 +1,19 @@
|
|||||||
Before we get into the topics for today I want to give a massive shout out to [Techworld with Nana](https://www.youtube.com/watch?v=yyUHQIec83I) and this fantastic concise journey through the fundamentals of Go.
|
Before we get into the topics for today I want to give a massive shout out to [Techworld with Nana](https://www.youtube.com/watch?v=yyUHQIec83I) and this fantastic concise journey through the fundamentals of Go.
|
||||||
|
|
||||||
On [Day8](day08.md) we set our environment up, on [Day9](day09.md) we walked through the Hello #90DaysOfDevOps code and on [Day10](day10.md)) we looked at our Go workspace and went into a little more deeper into compiling and running the code.
|
On [Day8](day08.md) we set our environment up, on [Day9](day09.md) we walked through the Hello #90DaysOfDevOps code and on [Day10](day10.md)) we looked at our Go workspace and went a little deeper into compiling and running the code.
|
||||||
|
|
||||||
Today we are going to take a look into Variables, Constants and Data Types whilst writing a new program.
|
Today we are going to take a look into Variables, Constants and Data Types whilst writing a new program.
|
||||||
|
|
||||||
## Variables & Constants in Go
|
## Variables & Constants in Go
|
||||||
Let's start by planning our application, I think it would be a good idea to work on a program that tells us how many days we have remaining in our #90DaysOfDevOps challenge.
|
Let's start by planning our application, I think it would be a good idea to work on a program that tells us how many days we have remained in our #90DaysOfDevOps challenge.
|
||||||
|
|
||||||
The first thing to consider here is that as we are building our app and we are welcoming our attendees and we are giving the user feedback on the amount of days they have completed we might use the term #90DaysOfDevOps a number of times throughout the program. This is a great use case to make #90DaysOfDevOps a variable within our program.
|
The first thing to consider here is that as we are building our app and we are welcoming our attendees and we are giving the user feedback on the number of days they have completed we might use the term #90DaysOfDevOps many times throughout the program. This is a great use case to make #90DaysOfDevOps a variable within our program.
|
||||||
|
|
||||||
- Variables are used to store values.
|
- Variables are used to store values.
|
||||||
- Like a little box with our saved information or values.
|
- Like a little box with our saved information or values.
|
||||||
- We can then use this variable across the program which also benefits that if this challenge or variable changes then we only have to change this in one place. Meaning we could translate this to other challenges we have in the community by just changing that one variable value.
|
- We can then use this variable across the program which also benefits that if this challenge or variable changes then we only have to change this in one place. Meaning we could translate this to other challenges we have in the community by just changing that one variable value.
|
||||||
|
|
||||||
In order to declare this in our Go Program we define a value by using a **keyword** for variables. This will live within our `func main` block of code that you will see later. You can find more about [Keywords](https://go.dev/ref/spec#Keywords)here.
|
To declare this in our Go Program we define a value by using a **keyword** for variables. This will live within our `func main` block of code that you will see later. You can find more about [Keywords](https://go.dev/ref/spec#Keywords)here.
|
||||||
|
|
||||||
Remember to make sure that your variable names are descriptive. If you declare a variable you must use it or you will get an error, this is to avoid possible dead code, code that is never used. This is the same for packages not used.
|
Remember to make sure that your variable names are descriptive. If you declare a variable you must use it or you will get an error, this is to avoid possible dead code, code that is never used. This is the same for packages not used.
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ You will then see from the below that we built our code with the above example a
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
We also know that our challenge is 90 days at least for this challenge, but next maybe its 100 so we want to define a variable to help us here as well. However for the purpose of our program we want to define this as a constant. Constants are like variables, except that their value cannot be changed within code (we can still create a new app later on down the line with this code and change this constant but this 90 will not change whilst we are running our application)
|
We also know that our challenge is 90 days at least for this challenge, but next, maybe it's 100 so we want to define a variable to help us here as well. However, for our program, we want to define this as a constant. Constants are like variables, except that their value cannot be changed within code (we can still create a new app later on down the line with this code and change this constant but this 90 will not change whilst we are running our application)
|
||||||
|
|
||||||
Adding the `const` to our code and adding another line of code to print this.
|
Adding the `const` to our code and adding another line of code to print this.
|
||||||
|
|
||||||
@ -61,9 +61,9 @@ If we then go through that `go build` process again and run you will see below t
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
Finally, and this won't be the end of our program we will come back to this in [Day12](day12.md) to add more functionality. We now want to add another variable for the number of days we have completed of the challenge.
|
Finally, and this won't be the end of our program we will come back to this in [Day12](day12.md) to add more functionality. We now want to add another variable for the number of days we have completed the challenge.
|
||||||
|
|
||||||
Below I added `dayscomplete` variable with the amount of days completed.
|
Below I added `dayscomplete` variable with the number of days completed.
|
||||||
|
|
||||||
```
|
```
|
||||||
package main
|
package main
|
||||||
@ -99,11 +99,11 @@ func main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Data Types
|
## Data Types
|
||||||
In the above examples we have not defined the type of variables, this is because we can give it a value here and Go is smart enough to know what that type is or at least can infer what it is based on the value you have stored. However if we want user input this will require a specific type.
|
In the above examples, we have not defined the type of variables, this is because we can give it a value here and Go is smart enough to know what that type is or at least can infer what it is based on the value you have stored. However, if we want a user to input this will require a specific type.
|
||||||
|
|
||||||
We have used Strings and Integers in our code so far. Integers for the number of days and strings are for the name of the challenge.
|
We have used Strings and Integers in our code so far. Integers for the number of days and strings are for the name of the challenge.
|
||||||
|
|
||||||
It is also important to note that each data type can do different things and behaves differently. For example integers can multiply where as strings do not.
|
It is also important to note that each data type can do different things and behaves differently. For example, integers can multiply where strings do not.
|
||||||
|
|
||||||
There are four categories
|
There are four categories
|
||||||
|
|
||||||
@ -112,14 +112,14 @@ There are four categories
|
|||||||
- **Reference type**: Pointers, slices, maps, functions, and channels come under this category.
|
- **Reference type**: Pointers, slices, maps, functions, and channels come under this category.
|
||||||
- **Interface type**
|
- **Interface type**
|
||||||
|
|
||||||
Data type is an important concept in programming. Data type specifies the size and type of variable values.
|
The data type is an important concept in programming. Data type specifies the size and type of variable values.
|
||||||
|
|
||||||
Go is statically typed, meaning that once a variable type is defined, it can only store data of that type.
|
Go is statically typed, meaning that once a variable type is defined, it can only store data of that type.
|
||||||
|
|
||||||
Go has three basic data types:
|
Go has three basic data types:
|
||||||
|
|
||||||
- **bool**: represents a boolean value and is either true or false
|
- **bool**: represents a boolean value and is either true or false
|
||||||
- **Numeric**: represents integer types, floating point values, and complex types
|
- **Numeric**: represents integer types, floating-point values, and complex types
|
||||||
- **string**: represents a string value
|
- **string**: represents a string value
|
||||||
|
|
||||||
I found this resource super detailed on data types [Golang by example](https://golangbyexample.com/all-data-types-in-golang-with-examples/)
|
I found this resource super detailed on data types [Golang by example](https://golangbyexample.com/all-data-types-in-golang-with-examples/)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
## Getting user input with Pointers and a finished program
|
## Getting user input with Pointers and a finished program
|
||||||
|
|
||||||
Yesterday ([Day 11](day11.md)), we created our first Go program that was self contained and the parts we wanted to really get user input for were created as variables within our code and given values, we now want to ask the user for their input to give the variable the value for the end message.
|
Yesterday ([Day 11](day11.md)), we created our first Go program that was self-contained and the parts we wanted to get user input for were created as variables within our code and given values, we now want to ask the user for their input to give the variable the value for the end message.
|
||||||
|
|
||||||
## Getting user input
|
## Getting user input
|
||||||
|
|
||||||
@ -12,9 +12,9 @@ Let's now add a new variable called `TwitterName` you can find this new code at
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
We are on day 12 and we would need to change that `dayscomplete` every day and compile our code each day if this was hard coded which doesn't sound so great.
|
We are on day 12 and we would need to change that `dayscomplete` every day and compile our code each day if this was hardcoded which doesn't sound so great.
|
||||||
|
|
||||||
Getting user input, we want to get the value of maybe a name and the amount of days completed. For us to do this we can use another function from within the `fmt` package.
|
Getting user input, we want to get the value of maybe a name and the number of days completed. For us to do this we can use another function from within the `fmt` package.
|
||||||
|
|
||||||
Recap on the `fmt` package, different functions for: formatted input and output (I/O)
|
Recap on the `fmt` package, different functions for: formatted input and output (I/O)
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ Let's now run our program and you see we have input for both of the above.
|
|||||||
|
|
||||||
Ok, that's great we got some user input and we printed a message but what about getting our program to tell us how many days we have left in our challenge.
|
Ok, that's great we got some user input and we printed a message but what about getting our program to tell us how many days we have left in our challenge.
|
||||||
|
|
||||||
In order for us to do that we have created a variable called `remainingDays` and we have hard valued this in our code as `90` we then need to change the value of this value to print out the remaining days when we get our user input of `DaysCompleted` we can do this with this simple variable change.
|
For us to do that we have created a variable called `remainingDays` and we have hard valued this in our code as `90` we then need to change the value of this value to print out the remaining days when we get our user input of `DaysCompleted` we can do this with this simple variable change.
|
||||||
|
|
||||||
```
|
```
|
||||||
remainingDays = remainingDays - DaysCompleted
|
remainingDays = remainingDays - DaysCompleted
|
||||||
@ -48,14 +48,13 @@ If we now run this program you can see that simple calculation is made based on
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## What is a pointer? (Special Variables)
|
## What is a pointer? (Special Variables)
|
||||||
|
|
||||||
A pointer is a (special) variable that points to the memory address of another variable.
|
A pointer is a (special) variable that points to the memory address of another variable.
|
||||||
|
|
||||||
A great explanation of this can be found here at [geeksforgeeks](https://www.geeksforgeeks.org/pointers-in-golang/)
|
A great explanation of this can be found here at [geeksforgeeks](https://www.geeksforgeeks.org/pointers-in-golang/)
|
||||||
|
|
||||||
Let's simplify our code now and show with and without the `&` in front of one of our print commands,this gives us the memory address of the pointer. I have added this code example here. [day12_example4.go](Go/day12_example4.go)
|
Let's simplify our code now and show with and without the `&` in front of one of our print commands, this gives us the memory address of the pointer. I have added this code example here. [day12_example4.go](Go/day12_example4.go)
|
||||||
|
|
||||||
Below is running this code.
|
Below is running this code.
|
||||||
|
|
||||||
@ -72,3 +71,4 @@ Below is running this code.
|
|||||||
- [Hitesh Choudhary - Complete playlist](https://www.youtube.com/playlist?list=PLRAV69dS1uWSR89FRQGZ6q9BR2b44Tr9N)
|
- [Hitesh Choudhary - Complete playlist](https://www.youtube.com/playlist?list=PLRAV69dS1uWSR89FRQGZ6q9BR2b44Tr9N)
|
||||||
|
|
||||||
See you on [Day 13](day13.md).
|
See you on [Day 13](day13.md).
|
||||||
|
|
||||||
|
306
Days/day13.md
@ -0,0 +1,306 @@
|
|||||||
|
## Tweet your progress with our new App
|
||||||
|
|
||||||
|
On the final day of looking into this programming language, we have only just touched the surface here of the language but it is that start that I think we need to get interested and excited and want to dive more into it.
|
||||||
|
|
||||||
|
Over the last few days, we have taken a small idea for an application and we have added functionality to it, in this session I want to take advantage of those packages we mentioned and create the functionality for our app to not only give you the update of your progress on screen but also send a tweet with the details of the challenge and your status.
|
||||||
|
|
||||||
|
## Adding the ability to tweet your progress
|
||||||
|
The first thing we need to do is set up our developer API access with Twitter for this to work.
|
||||||
|
|
||||||
|
Head to the [Twitter Developer Platform](https://developer.twitter.com) and sign in with your Twitter handle and details. Once in you should see something like the below without the app that I already have created.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
From here you may also want to request elevated access, this might take some time but it was very fast for me.
|
||||||
|
|
||||||
|
Next, we should select Projects & Apps and create our App. Limits are depending on the account access you have, with essential you only have one app and one project and with elevated you can have 3 apps.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Give your application a name
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
You will be then given these API tokens, it is important that you save these somewhere secure. (I have since deleted this app) We will need these later with our Go Application.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now we have our app created,(I did have to change my app name as the one in the screenshot above was already taken, these names need to be unique)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The keys that we gathered before are known as our consumer keys and we will also need our access token and secrets. We can gather this information using the "Keys & Tokens" tab.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Ok, we are done in the Twitter developer portal for now. Make sure you keep your keys safe because we will need them later.
|
||||||
|
|
||||||
|
## Go Twitter Bot
|
||||||
|
|
||||||
|
Remember the code we are starting within our application as well [day13_example1](Go/day13_example1.go) but first, we need to check we have the correct code to make something tweet
|
||||||
|
|
||||||
|
We now need to think about the code to get our output or message to Twitter in the form of a tweet. We are going to be using [go-twitter](https://github.com/dghubble/go-twitter) This is a Go client library for the Twitter API.
|
||||||
|
|
||||||
|
To test this before putting this into our main application, I created a new directory in our `src` folder called go-twitter-bot, issued the `go mod init github.com/michaelcade/go-twitter-bot on the folder which then created a `go.mod` file and then we can start writing our new main.go and test this out.
|
||||||
|
|
||||||
|
We now need those keys, tokens and secrets we gathered from the Twitter developer portal. We are going to set these in our environment variables. This will depend on the OS you are running:
|
||||||
|
|
||||||
|
Windows
|
||||||
|
```
|
||||||
|
set CONSUMER_KEY
|
||||||
|
set CONSUMER_SECRET
|
||||||
|
set ACCESS_TOKEN
|
||||||
|
set ACCESS_TOKEN_SECRET
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux / macOS
|
||||||
|
```
|
||||||
|
export CONSUMER_KEY
|
||||||
|
export CONSUMER_SECRET
|
||||||
|
export ACCESS_TOKEN
|
||||||
|
export ACCESS_TOKEN_SECRET
|
||||||
|
```
|
||||||
|
At this stage, you can take a look at [day13_example2](Go/day13_example2.go) at the code but you will see here that we are using a struct to define our keys, secrets and tokens.
|
||||||
|
|
||||||
|
We then have a `func` to parse those credentials and make that connection to the Twitter API
|
||||||
|
|
||||||
|
Then based on the success we will then send a tweet.
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// other imports
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/dghubble/go-twitter/twitter"
|
||||||
|
"github.com/dghubble/oauth1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Credentials stores all of our access/consumer tokens
|
||||||
|
// and secret keys needed for authentication against
|
||||||
|
// the twitter REST API.
|
||||||
|
type Credentials struct {
|
||||||
|
ConsumerKey string
|
||||||
|
ConsumerSecret string
|
||||||
|
AccessToken string
|
||||||
|
AccessTokenSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClient is a helper function that will return a twitter client
|
||||||
|
// that we can subsequently use to send tweets, or to stream new tweets
|
||||||
|
// this will take in a pointer to a Credential struct which will contain
|
||||||
|
// everything needed to authenticate and return a pointer to a twitter Client
|
||||||
|
// or an error
|
||||||
|
func getClient(creds *Credentials) (*twitter.Client, error) {
|
||||||
|
// Pass in your consumer key (API Key) and your Consumer Secret (API Secret)
|
||||||
|
config := oauth1.NewConfig(creds.ConsumerKey, creds.ConsumerSecret)
|
||||||
|
// Pass in your Access Token and your Access Token Secret
|
||||||
|
token := oauth1.NewToken(creds.AccessToken, creds.AccessTokenSecret)
|
||||||
|
|
||||||
|
httpClient := config.Client(oauth1.NoContext, token)
|
||||||
|
client := twitter.NewClient(httpClient)
|
||||||
|
|
||||||
|
// Verify Credentials
|
||||||
|
verifyParams := &twitter.AccountVerifyParams{
|
||||||
|
SkipStatus: twitter.Bool(true),
|
||||||
|
IncludeEmail: twitter.Bool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can retrieve the user and verify if the credentials
|
||||||
|
// we have used successfully allow us to log in!
|
||||||
|
user, _, err := client.Accounts.VerifyCredentials(verifyParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("User's ACCOUNT:\n%+v\n", user)
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Go-Twitter Bot v0.01")
|
||||||
|
creds := Credentials{
|
||||||
|
AccessToken: os.Getenv("ACCESS_TOKEN"),
|
||||||
|
AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
|
||||||
|
ConsumerKey: os.Getenv("CONSUMER_KEY"),
|
||||||
|
ConsumerSecret: os.Getenv("CONSUMER_SECRET"),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := getClient(&creds)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error getting Twitter Client")
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tweet, resp, err := client.Statuses.Update("A Test Tweet from the future, testing a #90DaysOfDevOps Program that tweets, tweet tweet", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
log.Printf("%+v\n", resp)
|
||||||
|
log.Printf("%+v\n", tweet)
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
The above will either give you an error based on what is happening or it will succeed and you will have a tweet sent with the message outlined in the code.
|
||||||
|
|
||||||
|
## Pairing the two together - Go-Twitter-Bot + Our App
|
||||||
|
|
||||||
|
Now we need to merge these two in our `main.go` I am sure someone out there is screaming that there is a better way of doing this and please comment on this as you can have more than one `.go` file in a project it might make sense but this works.
|
||||||
|
|
||||||
|
You can see the merged codebase [day13_example3](Go/day13_example3.go) but I will also show it below.
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// other imports
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/dghubble/go-twitter/twitter"
|
||||||
|
"github.com/dghubble/oauth1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Credentials stores all of our access/consumer tokens
|
||||||
|
// and secret keys needed for authentication against
|
||||||
|
// the twitter REST API.
|
||||||
|
type Credentials struct {
|
||||||
|
ConsumerKey string
|
||||||
|
ConsumerSecret string
|
||||||
|
AccessToken string
|
||||||
|
AccessTokenSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClient is a helper function that will return a twitter client
|
||||||
|
// that we can subsequently use to send tweets, or to stream new tweets
|
||||||
|
// this will take in a pointer to a Credential struct which will contain
|
||||||
|
// everything needed to authenticate and return a pointer to a twitter Client
|
||||||
|
// or an error
|
||||||
|
func getClient(creds *Credentials) (*twitter.Client, error) {
|
||||||
|
// Pass in your consumer key (API Key) and your Consumer Secret (API Secret)
|
||||||
|
config := oauth1.NewConfig(creds.ConsumerKey, creds.ConsumerSecret)
|
||||||
|
// Pass in your Access Token and your Access Token Secret
|
||||||
|
token := oauth1.NewToken(creds.AccessToken, creds.AccessTokenSecret)
|
||||||
|
|
||||||
|
httpClient := config.Client(oauth1.NoContext, token)
|
||||||
|
client := twitter.NewClient(httpClient)
|
||||||
|
|
||||||
|
// Verify Credentials
|
||||||
|
verifyParams := &twitter.AccountVerifyParams{
|
||||||
|
SkipStatus: twitter.Bool(true),
|
||||||
|
IncludeEmail: twitter.Bool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can retrieve the user and verify if the credentials
|
||||||
|
// we have used successfully allow us to log in!
|
||||||
|
user, _, err := client.Accounts.VerifyCredentials(verifyParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("User's ACCOUNT:\n%+v\n", user)
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
creds := Credentials{
|
||||||
|
AccessToken: os.Getenv("ACCESS_TOKEN"),
|
||||||
|
AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
|
||||||
|
ConsumerKey: os.Getenv("CONSUMER_KEY"),
|
||||||
|
ConsumerSecret: os.Getenv("CONSUMER_SECRET"),
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const DaysTotal int = 90
|
||||||
|
var remainingDays uint = 90
|
||||||
|
challenge := "#90DaysOfDevOps"
|
||||||
|
|
||||||
|
fmt.Printf("Welcome to the %v challenge.\nThis challenge consists of %v days\n", challenge, DaysTotal)
|
||||||
|
|
||||||
|
var TwitterName string
|
||||||
|
var DaysCompleted uint
|
||||||
|
|
||||||
|
// asking for user input
|
||||||
|
fmt.Println("Enter Your Twitter Handle: ")
|
||||||
|
fmt.Scanln(&TwitterName)
|
||||||
|
|
||||||
|
fmt.Println("How many days have you completed?: ")
|
||||||
|
fmt.Scanln(&DaysCompleted)
|
||||||
|
|
||||||
|
// calculate remaining days
|
||||||
|
remainingDays = remainingDays - DaysCompleted
|
||||||
|
|
||||||
|
//fmt.Printf("Thank you %v for taking part and completing %v days.\n", TwitterName, DaysCompleted)
|
||||||
|
//fmt.Printf("You have %v days remaining for the %v challenge\n", remainingDays, challenge)
|
||||||
|
// fmt.Println("Good luck")
|
||||||
|
|
||||||
|
client, err := getClient(&creds)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error getting Twitter Client, this is expected if you did not supply your Twitter API tokens")
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf("Hey I am %v I have been doing the %v for %v days and I have %v Days left", TwitterName, challenge, DaysCompleted, remainingDays)
|
||||||
|
tweet, resp, err := client.Statuses.Update(message, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
log.Printf("%+v\n", resp)
|
||||||
|
log.Printf("%+v\n", tweet)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The outcome of this should be a tweet but if you did not supply your environment variables then you should get an error like the one below.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Once you have fixed that or if you choose not to authenticate with Twitter then you can use the code we finished with yesterday. The terminal output on success will look similar to this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The resulting tweet should look something like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## How to compile for multiple OSs
|
||||||
|
|
||||||
|
I next want to cover the question, "How do you compile for multiple Operating Systems?" The great thing about Go is that it can easily compile for many different Operating Systems. You can get a full list by running the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
go tool dist list
|
||||||
|
```
|
||||||
|
Using our `go build` commands so far is great and it will use the `GOOS` and `GOARCH` environment variables to determine the host machine and what the build should be built for. But we can also create other binaries by using the code below as an example.
|
||||||
|
|
||||||
|
```
|
||||||
|
GOARCH=amd64 GOOS=darwin go build -o ${BINARY_NAME}_0.1_darwin main.go
|
||||||
|
GOARCH=amd64 GOOS=linux go build -o ${BINARY_NAME}_0.1_linux main.go
|
||||||
|
GOARCH=amd64 GOOS=windows go build -o ${BINARY_NAME}_0.1_windows main.go
|
||||||
|
GOARCH=arm64 GOOS=linux go build -o ${BINARY_NAME}_0.1_linux_arm64 main.go
|
||||||
|
GOARCH=arm64 GOOS=darwin go build -o ${BINARY_NAME}_0.1_darwin_arm64 main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
This will then give you binaries in your directory for all of the above platforms. You can then take this and create a makefile to build these binaries whenever you add new features and functionality to your code. I have included the [makefile](Go/makefile)
|
||||||
|
|
||||||
|
This is what I have used to create the releases you can now see on the [repository](https://github.com/MichaelCade/90DaysOfDevOps/releases)
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [StackOverflow 2021 Developer Survey](https://insights.stackoverflow.com/survey/2021)
|
||||||
|
- [Why we are choosing Golang to learn](https://www.youtube.com/watch?v=7pLqIIAqZD4&t=9s)
|
||||||
|
- [Jake Wright - Learn Go in 12 minutes](https://www.youtube.com/watch?v=C8LgvuEBraI&t=312s)
|
||||||
|
- [Techworld with Nana - Golang full course - 3 hours 24 mins](https://www.youtube.com/watch?v=yyUHQIec83I)
|
||||||
|
- [**NOT FREE** Nigel Poulton Pluralsight - Go Fundamentals - 3 hours 26 mins](https://www.pluralsight.com/courses/go-fundamentals)
|
||||||
|
- [FreeCodeCamp - Learn Go Programming - Golang Tutorial for Beginners](https://www.youtube.com/watch?v=YS4e4q9oBaU&t=1025s)
|
||||||
|
- [Hitesh Choudhary - Complete playlist](https://www.youtube.com/playlist?list=PLRAV69dS1uWSR89FRQGZ6q9BR2b44Tr9N)
|
||||||
|
- [A great repo full of all things DevOps & exercises](https://github.com/bregman-arie/devops-exercises)
|
||||||
|
- [GoByExample - Example based learning](https://gobyexample.com/)
|
||||||
|
- [go.dev/tour/list](https://go.dev/tour/list)
|
||||||
|
- [go.dev/learn](https://go.dev/learn/)
|
||||||
|
|
||||||
|
This wraps up the Programming language for 7 days! So much more that can be covered and I hope you have been able to continue through the content above and be able to understand some of the other aspects of the Go programming language.
|
||||||
|
|
||||||
|
Next, we take our focus into Linux and some of the fundamentals that we should all know there.
|
||||||
|
|
||||||
|
See you on [Day 14](day14.md).
|
@ -28,7 +28,7 @@ This will not cover all things DevOps but it will cover the areas that I feel wi
|
|||||||
- [ ] ⌨️ 10 > [The Go Workspace & Compiling & running code](Days/day10.md)
|
- [ ] ⌨️ 10 > [The Go Workspace & Compiling & running code](Days/day10.md)
|
||||||
- [ ] ⌨️ 11 > [Variables, Constants & Data Types](Days/day11.md)
|
- [ ] ⌨️ 11 > [Variables, Constants & Data Types](Days/day11.md)
|
||||||
- [ ] ⌨️ 12 > [Getting user input with Pointers and a finished program](Days/day12.md)
|
- [ ] ⌨️ 12 > [Getting user input with Pointers and a finished program](Days/day12.md)
|
||||||
- [ ] ⌨️ 13 > [](Days/day13.md)
|
- [ ] ⌨️ 13 > [Tweet your progress with our new App](Days/day13.md)
|
||||||
|
|
||||||
### Knowing Linux Basics
|
### Knowing Linux Basics
|
||||||
|
|
||||||
|