├── .gitignore
├── .idea
├── detekt.xml
├── kotlinc.xml
├── ktfmt.xml
├── misc.xml
└── vcs.xml
├── 0.hello.main.kts
├── 1.shell-commands.main.kts
├── 2.files-csv.main.kts
├── 2.files-json.main.kts
├── 2.files.main.kts
├── 3.input-from-shell.main.kts
├── 4.coroutines.main.kts
├── 5.pluck-tags-from-blog-posts.main.kts
├── 6.tootbot.main.kts
├── 7.carrot-wars-tabulate.main.kts
├── 8.embed-external-scripts.main.kts
├── 9.curl-vs-okhttp-api-system-env-json-parse-gitlab.main.kts
├── LICENSE
├── README.md
├── common.main.kts
├── feed.json
├── star-wars-demo-results.csv
├── toots.csv
└── x.test.main.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | ### macOS template
3 |
4 | # General
5 | .DS_Store
6 | .AppleDouble
7 | .LSOverride
8 |
9 | # Icon must end with two \r
10 | Icon
11 |
12 | # Thumbnails
13 | ._*
14 |
15 | # Files that might appear in the root of a volume
16 | .DocumentRevisions-V100
17 | .fseventsd
18 | .Spotlight-V100
19 | .TemporaryItems
20 | .Trashes
21 | .VolumeIcon.icns
22 | .com.apple.timemachine.donotpresent
23 |
24 | # Directories potentially created on remote AFP share
25 | .AppleDB
26 | .AppleDesktop
27 | Network Trash Folder
28 | Temporary Items
29 | .apdisk
30 |
31 | ##############################################################################
32 | ### Windows template
33 |
34 | # Windows thumbnail cache files
35 | Thumbs.db
36 | Thumbs.db:encryptable
37 | ehthumbs.db
38 | ehthumbs_vista.db
39 |
40 | # Dump file
41 | *.stackdump
42 |
43 | # Folder config file
44 | [Dd]esktop.ini
45 |
46 | # Recycle Bin used on file shares
47 | $RECYCLE.BIN/
48 |
49 | # Windows Installer files
50 | *.cab
51 | *.msi
52 | *.msix
53 | *.msm
54 | *.msp
55 |
56 | # Windows shortcuts
57 | *.lnk
58 |
59 | ##############################################################################
60 | ### Linux template
61 | *~
62 |
63 | # temporary files which can be created if a process still has a handle open of a deleted file
64 | .fuse_hidden*
65 |
66 | # KDE directory preferences
67 | .directory
68 |
69 | # Linux trash folder which might appear on any partition or disk
70 | .Trash-*
71 |
72 | # .nfs files are created when an open file is removed but is still being accessed
73 | .nfs*
74 |
75 | ##############################################################################
76 | ### Generic Files (across platforms)
77 |
78 | # Log file
79 | *.log
80 | *.log.txt
81 |
82 | # CONVENIENCE
83 | *.ignore
84 |
85 | ##############################################################################
86 | ### Visual Studio template
87 |
88 | # Visual studio project files
89 | *.vcxproj
90 | *.vcxproj.filters
91 | *.sln
92 |
93 | # Visual Studio Code files
94 | .vscode
95 |
96 | ##############################################################################
97 | ### JetBrains template
98 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
99 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
100 |
101 | *.iml
102 | *.ipr
103 | *.iws
104 | /.idea/*
105 |
106 | # Don't gitignore project specific files
107 | # ref: https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#system-directory
108 |
109 | !/.idea/copyright/
110 | !/.idea/encodings.xml
111 | !/.idea/fileTemplates/
112 | !/.idea/inspectionProfiles/
113 | !/.idea/scopes/
114 | !/.idea/vcs.xml
115 |
116 | # customized code style schemes
117 | !/.idea/codeStyles/
118 |
119 | # Intellij Project Icon
120 | !.idea/icon.svg
121 |
122 | # Intellij Project Name
123 | !.idea/.name
124 |
125 | # contains compiler special arguments (like -Xallow-any-scripts-in-source-roots) for kotlin 1.9
126 | !.idea/kotlinc.xml
127 |
128 | # project jdk config
129 | !.idea/misc.xml
130 |
131 | ##############################################################################
132 | ### Gradle template
133 |
134 | .gradle
135 | .gradle/
136 | # Note that you may need to exclude by hand other folders
137 | # named build if necessary (e.g., in src/)
138 | /build/
139 | **/build/
140 | !src/**/build/
141 |
142 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
143 | !gradle/wrapper/gradle-wrapper.jar
144 |
145 | # Cache of project
146 | .gradletasknamecache
147 |
148 | # Local configuration file (sdk path, etc)
149 | local.properties
150 |
151 | ##############################################################################
152 | ### Bazel template
153 | /bazel-out
154 | /bazel-bin
155 | /bazel-caper-repo
156 | /bazel-testlogs
157 | /bazel-*monorepo*
158 | .ijwb
159 | .clwb
160 | .cache
161 | .bazelrc-ci
162 | .bazelrc-user
163 |
164 | ##############################################################################
165 | ### Node modules npm
166 |
167 | node_modules/
168 |
169 | ##############################################################################
170 | ### C++ template
171 |
172 | # Compiled class file
173 | *.cxx
174 |
175 | # Logs
176 | CMakeFiles/*-log.txt
177 |
178 | ##############################################################################
179 | ### Python template
180 |
181 | venv/
182 |
183 | ##############################################################################
184 | ### Kotlin template
185 |
186 | # Compiled class file
187 | *.class
188 |
189 | # Package Files #
190 | *.jar
191 | *.war
192 | *.nar
193 | *.ear
194 | *.zip
195 | *.tar.gz
196 | *.rar
197 |
198 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
199 | hs_err_pid*
200 |
201 | ##############################################################################
202 | ### Android-specific stuff
203 |
204 | # Built application files
205 | *.apk
206 | *.ap_
207 | *.aab
208 | *.apks
209 |
210 | # Files for the Dalvik VM
211 | *.dex
212 |
213 | # Generated files
214 | bin/
215 | gen/
216 | obj/
217 |
218 | # Google/Firebase secrets
219 | google-services.json
220 |
221 | # Android Studio generated files and folders
222 | captures/
223 | .externalNativeBuild/
224 | .cxx/
225 | output.json
226 |
227 | # Keystore files
228 | *.jks
229 | *.keystore
230 |
231 | # Android Profiling
232 | *.hprof
233 |
234 | ##############################################################################
235 | ### SECRETS
236 |
237 | ##############################################################################
238 | ### Project Specific
239 |
240 | ## Jetbrains specific
241 |
242 | # controls plugin dependencies (like ktmfmt/detekt)
243 | !/.idea/externalDependencies.xml
244 | # Plugins used in project
245 | !.idea/detekt.xml
246 | !.idea/ktfmt.xml
247 |
248 | # to start clean, remove the ".default" suffix in these xmls
249 | # and replace your existing ones with them
250 |
251 | # List of gradle projects supported (right tab)
252 | !/.idea/gradle.default.xml
253 |
254 | # List of disabled projects (loading too many will crash Intellij)
255 | !/.idea/compiler.default.xml
256 |
257 | # Controls Project View (left tab)
258 | !/.idea/modules.default.xml
259 |
--------------------------------------------------------------------------------
/.idea/detekt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/ktfmt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/0.hello.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | /*
4 | * This script is a starter template script.
5 | * It takes in whatever you send as arguments and prints them out.
6 | *
7 | * run this script like so:
8 |
9 | $> brew install kotlin
10 | $> kotlin 0.hello.main.kts Hello 🌎
11 | *
12 | * alternatively if you want to make the script executable
13 |
14 | $> chmod +x 0.hello.main.kts
15 | $> ./0.hello.main.kts Hello 🌎
16 | */
17 |
18 | println("Hello from Kotlin Scripts!")
19 | println("******* PROGRAM START ***************** ")
20 | program(args)
21 | println("******* PROGRAM END ***************** ")
22 |
23 |
24 | fun program(args: Array) {
25 | println("\uD83D\uDEE0️ This program received the following arguments: ${args.joinToString()} ")
26 | }
27 |
--------------------------------------------------------------------------------
/1.shell-commands.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 | import java.io.BufferedReader
3 | import java.util.concurrent.TimeUnit
4 | import kotlin.system.exitProcess
5 |
6 | /*
7 | * This script
8 | *
9 | * To run kscripts do it like so:
10 | $> brew install kotlin
11 | $> kotlin .main.kts
12 | *
13 | * Alternatively, make this script executable
14 | $> chmod +x .main.kts
15 | $> ./.main.kts
16 | *
17 | * Read more about this at https://kau.sh/blog/kscript
18 | */
19 |
20 | val ANSI_RESET = "\u001B[0m"
21 | val ANSI_GRAY = "\u001B[90m" // use this mostly
22 | val ANSI_PURPLE = "\u001B[35m" // input commands
23 | val ANSI_GREEN = "\u001B[32m" // highlighting values
24 | val ANSI_RED = "\u001B[31m" // error
25 | val ANSI_YELLOW = "\u001B[33m" // important messages
26 | //val ANSI_BLUE = "\u001B[34m"
27 | //val ANSI_CYAN = "\u001B[36m"
28 | //val ANSI_WHITE = "\u001B[37m"
29 |
30 | val DEBUG = true
31 |
32 | if (DEBUG) println("$ANSI_GRAY ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ PROGRAM START$ANSI_RESET")
33 | program(args)
34 | if (DEBUG) println("$ANSI_GRAY ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ PROGRAM END $ANSI_RESET")
35 |
36 | fun program(args: Array) {
37 |
38 | if (DEBUG) println("$ANSI_GRAY[args]$ANSI_GREEN${args.joinToString()}$ANSI_RESET")
39 |
40 | // notice use of unix shell commands
41 | "find scratch.txt".exec()
42 | "rm -rf scratch.txt".exec()
43 | "find scratch.txt".exec()
44 | "touch scratch.txt".exec()
45 |
46 | // find all files ending with ".main.kts"
47 | // in this directory recursively
48 | // directory path must contain "res/values"
49 | // split the output by newline and filename in a list
50 | val fileList =
51 | "find . -type f -path '*.main.kts'"
52 | .exec()
53 | .split("\n")
54 | .filter { it.isNotBlank() }
55 |
56 | println("$ANSI_GRAY Found the following files: $ANSI_RESET")
57 | fileList.forEach {
58 | println("$ANSI_GREEN - $it $ANSI_RESET")
59 | }
60 |
61 | }
62 |
63 |
64 | fun String.exec(
65 | exitOnError: Boolean = false,
66 | verbose: Boolean = true,
67 | timeoutInMinutes: Long = 3,
68 | ): String {
69 | if (verbose) println("${ANSI_GRAY}[command] $this ${ANSI_RESET}")
70 | val process =
71 | ProcessBuilder()
72 | // .directory(workingDirectory)
73 | .redirectErrorStream(true)
74 | .redirectOutput(ProcessBuilder.Redirect.PIPE)
75 | .redirectError(ProcessBuilder.Redirect.PIPE)
76 | // /bin/bash -c -l necessary to use programs like "find"
77 | // from your real user shell environment
78 | .command("/bin/bash", "-c", "-l", this)
79 | .start()
80 |
81 | process.waitFor(timeoutInMinutes, TimeUnit.MINUTES)
82 | return process.retrieveOutput(exitOnError, verbose)
83 | }
84 |
85 | private fun Process.retrieveOutput(exitOnError: Boolean, verbose: Boolean): String {
86 | val outputText = inputStream.bufferedReader().use(BufferedReader::readText)
87 | val exitCode = exitValue()
88 |
89 | val color: String
90 | val sign: String
91 | when {
92 | exitOnError -> {
93 | color = ANSI_RED
94 | sign = "✗ | err: $exitCode"
95 | }
96 | exitCode != 0 -> {
97 | color = ANSI_YELLOW
98 | sign = "⚠️ | err: $exitCode"
99 | }
100 | else -> {
101 | color = ANSI_GRAY
102 | sign = ""
103 | }
104 | }
105 |
106 | if (verbose || exitCode != 0) {
107 | println(
108 | """$color[output] $sign:
109 | ${outputText.trim()}
110 | $ANSI_RESET""",
111 | )
112 | }
113 |
114 | if (exitOnError) {
115 | println("${ANSI_RED}✗ Exiting... $ANSI_RESET")
116 | exitProcess(1)
117 | }
118 | return outputText.trim()
119 | }
120 |
--------------------------------------------------------------------------------
/2.files-csv.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:DependsOn("com.squareup.okio:okio:3.3.0")
4 |
5 | import okio.FileSystem
6 | import okio.IOException
7 | import okio.Path
8 | import okio.Path.Companion.toPath
9 |
10 | /*
11 | * This script shows you how you can read a csv file.
12 | * run this script like so:
13 |
14 | $> brew install kotlin
15 | $> chmod +x 2.csv.main.kts
16 | $> ./2.csv.main.kts
17 | */
18 |
19 | println("\uD83D\uDEE0️ ****** Reading from CSV File ****** \uD83D\uDEE0")
20 |
21 | val filePath = "star-wars-demo-results.csv".toPath()
22 |
23 | println(" --- Start of file --- 🏁")
24 | forEachLine(filePath) { line ->
25 | println(line)
26 | }
27 | println("--- End of file --- ✅")
28 |
29 | @Throws(IOException::class)
30 | fun forEachLine(path: Path, lineAction: (String) -> Unit) {
31 | FileSystem.SYSTEM.read(path) {
32 | while (true) {
33 | val line = readUtf8Line() ?: break
34 | lineAction.invoke(line)
35 | }
36 | }
37 | }
38 |
39 |
40 |
41 | println("\uD83D\uDEE0️ ****** Writing to a CSV File ****** \uD83D\uDEE0")
42 |
43 | val tootsFile = "./toots.csv".toPath()
44 | var tooted = mutableListOf()
45 |
46 | println(" --- ☝\uD83C\uDFFD Reading from another existing CSV file ")
47 | forEachLine(tootsFile) { line ->
48 | val (path, tootId) = line.split(',', ignoreCase = false, limit = 2)
49 | tooted.add(Tooted(path, tootId))
50 | }
51 | println("existing CSV file has ${tooted.count()} toots/lines")
52 |
53 | println("--- Writing back to the CSV file --- ✅")
54 | FileSystem.SYSTEM.write(tootsFile) {
55 | tooted.forEach { tooted ->
56 | writeUtf8(tooted.postId)
57 | writeUtf8(",")
58 | writeUtf8(tooted.tootId)
59 | writeUtf8("\n")
60 |
61 | // simply write into it again (so you can see the change in file)
62 | writeUtf8(tooted.postId)
63 | writeUtf8(",")
64 | writeUtf8(tooted.tootId)
65 | writeUtf8("\n")
66 | }
67 | }
68 |
69 | data class Tooted(
70 | val postId: String,
71 | val tootId: String,
72 | )
73 |
--------------------------------------------------------------------------------
/2.files-json.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:Repository("https://repo.maven.apache.org/maven2/")
4 | @file:DependsOn("com.squareup.okhttp3:okhttp:4.10.0")
5 | @file:DependsOn("com.squareup.okio:okio:3.9.0")
6 | @file:DependsOn("com.squareup.moshi:moshi-kotlin:1.15.1")
7 | @file:DependsOn("com.squareup.moshi:moshi-adapters:1.13.0")
8 | @file:DependsOn("dev.zacsweers.moshix:moshi-metadata-reflect:0.27.1")
9 |
10 | import com.squareup.moshi.Json
11 | import com.squareup.moshi.Moshi
12 | import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
13 | import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory
14 | import okhttp3.OkHttpClient
15 | import okio.FileSystem
16 | import okio.Path.Companion.toPath
17 | import okio.buffer
18 | import java.util.*
19 | import okhttp3.Request
20 |
21 | /*
22 | * This script shows you how you can read a json file
23 | * then parse it into a Kotlin data classes.
24 | * It also shows you how to download a json file from the web
25 | *
26 | * run this script like so:
27 |
28 | $> brew install kotlin
29 | $> chmod +x 3.json.main.kts
30 | $> ./3.json.main.kts
31 | */
32 |
33 |
34 | /*
35 | * ****************************
36 | * Read from local json file
37 | * ****************************
38 | */
39 | val jsonFeedFilePath = "./feed.json".toPath()
40 |
41 | println("\uD83D\uDEE0️ ****** Load json file from \uD83D\uDCBE ****** \uD83D\uDEE0")
42 | val jsonFile: String = FileSystem.SYSTEM
43 | .source(jsonFeedFilePath)
44 | .buffer()
45 | .readUtf8()
46 |
47 | val moshi: Moshi = Moshi.Builder()
48 | .add(MetadataKotlinJsonAdapterFactory())
49 | .add(Date::class.java, Rfc3339DateJsonAdapter())
50 | .build()
51 |
52 | var feed: Feed = moshi
53 | .adapter(Feed::class.java)
54 | .fromJson(jsonFile) as Feed
55 |
56 | println("🤖 local feed has ${feed.pages.count()} pages")
57 |
58 | /*
59 | * ****************************
60 | * Download RSS json file
61 | * ****************************
62 | */
63 | val jsonFeedUrl = "https://kau.sh/blog/feed.json"
64 |
65 | println("\uD83D\uDEE0️ ****** Download blog feed json file from \uD83C\uDF0D ****** \uD83D\uDEE0")
66 |
67 | OkHttpClient()
68 | .newCall(
69 | Request.Builder()
70 | .url(jsonFeedUrl)
71 | .build(),
72 | )
73 | .execute()
74 | .use { response ->
75 | feed = moshi
76 | .adapter(Feed::class.java)
77 | .fromJson(response.body!!.source()) as Feed
78 | }
79 |
80 | println("🤖 remote found ${feed.pages.count()} pages")
81 |
82 | data class Page(
83 | val title: String,
84 | @Json(name = "date_published")
85 | val publishedDate: Date,
86 | @Json(name = "file_path")
87 | val filePath: String?,
88 | val description: String?,
89 | )
90 |
91 | data class Feed(
92 | val title: String,
93 | val description: String,
94 | @Json(name = "items")
95 | val pages: List
96 | )
97 |
98 |
--------------------------------------------------------------------------------
/2.files.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:DependsOn("com.squareup.okio:okio:3.5.0")
4 |
5 | import okio.BufferedSink
6 | import okio.FileSystem
7 | import okio.Path
8 | import okio.Path.Companion.toOkioPath
9 | import okio.Path.Companion.toPath
10 | import okio.appendingSink
11 | import okio.buffer
12 | import okio.source
13 | import java.io.File
14 |
15 |
16 | /*
17 | * This script shows how to deal with files in kotlin scripts.
18 |
19 | * In this file you'll see how to:
20 | * 1. read each line from a file
21 | * 2. write to a file
22 | * 3. download a file from the web
23 | *
24 | * To run kscripts do it like so:
25 | $> brew install kotlin
26 | $> kotlin .main.kts
27 | *
28 | * Alternatively, make this script executable
29 | $> chmod +x .main.kts
30 | $> ./.main.kts
31 | *
32 | * Read more about this at https://kau.sh/blog/kscript
33 | */
34 |
35 | val ANSI_RESET = "\u001B[0m"
36 | val ANSI_GRAY = "\u001B[90m" // use this mostly
37 | val ANSI_PURPLE = "\u001B[35m" // input commands
38 | val ANSI_GREEN = "\u001B[32m" // highlighting values
39 | val ANSI_RED = "\u001B[31m" // error
40 | val ANSI_YELLOW = "\u001B[33m" // important messages
41 | // val ANSI_BLUE = "\u001B[34m"
42 | // val ANSI_CYAN = "\u001B[36m"
43 | // val ANSI_WHITE = "\u001B[37m"
44 |
45 | val DEBUG = true
46 |
47 | if (DEBUG) println("$ANSI_GRAY ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ PROGRAM START$ANSI_RESET")
48 | program(args)
49 | if (DEBUG) println("$ANSI_GRAY ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ PROGRAM END $ANSI_RESET")
50 |
51 | fun program(args: Array) {
52 | if (DEBUG) println("$ANSI_GRAY[args]$ANSI_GREEN${args.joinToString()}$ANSI_RESET")
53 |
54 | readEachLine()
55 | println("\n\n")
56 | createFileWithContent()
57 | println("\n\n")
58 | appendContentToFile()
59 |
60 | }
61 |
62 | fun readEachLine() {
63 | println("${ANSI_GRAY}🚧 printing first 5 lines of feed.json $ANSI_RESET")
64 | // print the first 10 lines of a file
65 | forEachLine("feed.json".toPath()) { text: String, index: Int ->
66 | // if (index !in 1424..1505) return@forEachLine
67 | if(index > 10) return@forEachLine
68 | println("$ANSI_GRAY Line $index: $ANSI_GREEN $text $ANSI_RESET")
69 | }
70 | }
71 |
72 | fun createFileWithContent() {
73 | val filename = "scratch.txt"
74 | val file = File(filename)
75 |
76 | if (file.exists()) {
77 | println("${ANSI_GRAY}🚧 $file already exists. going to delete it $ANSI_RESET")
78 | file.delete()
79 | }
80 |
81 | println("${ANSI_GRAY}🚧 creating $file $ANSI_RESET")
82 | file.createNewFile()
83 |
84 | writeToFile(file.toOkioPath()) { sink ->
85 | sink.writeUtf8("---- head of file ---- \n")
86 | sink.writeUtf8("... riveting content\n")
87 | sink.writeUtf8("---- tail of file ---- \n")
88 | }
89 |
90 | println("$ANSI_YELLOW ✓ open $file to read some riveting content $ANSI_RESET")
91 | }
92 |
93 |
94 | fun appendContentToFile() {
95 | val filename = "scratch.txt"
96 | val file = File(filename)
97 | file.appendingSink().buffer().use {
98 | it.writeUtf8("*** appended content ***")
99 | }
100 | println("$ANSI_YELLOW ✓ appended content to $file $ANSI_RESET")
101 | }
102 |
103 | /**
104 | * Okio recipe to read a file line by line
105 | * https://square.github.io/okio/recipes/#read-a-text-file-line-by-line-javakotlin
106 | */
107 | fun forEachLine(path: Path, lineAction: (String, Int) -> Unit) {
108 | var index: Int = 0
109 | FileSystem.SYSTEM.read(path) {
110 | loop@ while (true) {
111 | val line = readUtf8Line() ?: break
112 | lineAction.invoke(line, index)
113 | index += 1
114 | }
115 | }
116 | }
117 |
118 | /**
119 | * Okio recipe to write to a file
120 | * https://square.github.io/okio/recipes/#read-a-text-file-line-by-line-javakotlin
121 | */
122 | fun writeToFile(path: Path, writingSink: (BufferedSink) -> Unit) {
123 | FileSystem.SYSTEM.write(path) { writingSink.invoke(this) }
124 | }
125 |
--------------------------------------------------------------------------------
/3.input-from-shell.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:Repository("https://jitpack.io")
4 | @file:DependsOn("com.github.kaushikgopal:shell.main.kts:276950a346")
5 |
6 | import sh.kau.shell.ShellConsole.Companion.ANSI_GRAY
7 | import sh.kau.shell.ShellConsole.Companion.ANSI_GREEN
8 | import sh.kau.shell.ShellConsole.Companion.ANSI_PURPLE
9 | import sh.kau.shell.ShellConsole.Companion.ANSI_RESET
10 | import sh.kau.shell.ShellConsole.Companion.ANSI_YELLOW
11 | import sh.kau.shell.runInShell
12 |
13 | /*
14 | * This script demonstrates how you can take inputs from the user
15 | * through an interactive way by prompting users for inputs step by step.
16 | *
17 | * There's a few concepts being demonstrated here:
18 | * 1. how to print things in color
19 | * 2. how to take user inputs from the command line
20 | * 3. how to execute shell commands from within a kotlin script
21 | *
22 | * To run kscripts do it like so:
23 | $> brew install kotlin
24 | $> kotlin 3.input-from-shell.main.kts
25 | *
26 | * Alternatively, make this script executable
27 | $> chmod +x .main.kts
28 | $> ./.main.kts
29 | *
30 | * Read more about this at https://kau.sh/blog/kscript
31 | */
32 |
33 | println("$ANSI_GRAY ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ PROGRAM START$ANSI_RESET")
34 | program(args)
35 | println("$ANSI_GRAY ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ PROGRAM END $ANSI_RESET")
36 |
37 | fun program(args: Array) {
38 | println("$ANSI_GRAY[args]$ANSI_GREEN${args.joinToString()}$ANSI_RESET")
39 |
40 | // simple command - taking an input
41 | // print it back out
42 |
43 | print("\n\n$ANSI_PURPLE Enter your name: $ANSI_RESET")
44 | val name = readlnOrNull()
45 | println("\n\n$ANSI_GRAY Why hello $ANSI_YELLOW $name! $ANSI_RESET\n\n\n")
46 |
47 |
48 | // take a domain name
49 | // open social media links such as http://threads.
50 |
51 | print("Do you have a domain name? (y/N): ")
52 | val hasDomain = readlnOrNull()
53 |
54 | var domain: String? = null
55 | if (hasDomain == "y") {
56 | print("What is your domain name: ")
57 | domain = readlnOrNull()
58 | }
59 |
60 | domain = domain ?: "kau.sh"
61 |
62 | println("${ANSI_GRAY}🚧 Opening all the social profiles now!$ANSI_RESET")
63 |
64 | // execute a shell command
65 | listOf(
66 | "open https://threads.$domain",
67 | "open https://github.$domain",
68 | "open https://twitter.$domain",
69 | "open https://mastodon.$domain",
70 | "open https://instagram.$domain"
71 | ).forEach { it.runInShell() }
72 | }
73 |
--------------------------------------------------------------------------------
/4.coroutines.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:Repository("https://repo.maven.apache.org/maven2/")
4 | @file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2")
5 | @file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.2")
6 |
7 | import kotlin.system.measureTimeMillis
8 | import kotlinx.coroutines.*
9 |
10 |
11 | /*
12 | * This script shows you how you can use coroutines to do async programming
13 | *
14 | * run this script like so:
15 |
16 | $> brew install kotlin
17 | $> chmod +x 4.coroutines.main.kts
18 | $> ./4.coroutines.main.kts
19 | */
20 |
21 | //main()
22 | fun main() = runBlocking {
23 | println("------ first program ")
24 | println("------ two coroutines run in series")
25 |
26 | val time = measureTimeMillis {
27 | val one = doOne()
28 | val two = doTwo()
29 | println("One and Two is ${one + two}")
30 | }
31 |
32 | println("finished in $time")
33 | }
34 |
35 |
36 |
37 | //main2()
38 | fun main2() = runBlocking {
39 | println("------ second program ")
40 | println("------ multiple coroutines non-waiting")
41 |
42 | launch {
43 | delay(200L)
44 | println("---- A")
45 | }
46 |
47 | coroutineScope {
48 | launch {
49 | delay(500L)
50 | println("---- B")
51 | }
52 |
53 | delay(100L)
54 | println("---- C")
55 | }
56 |
57 | println("---- D")
58 | }
59 |
60 |
61 |
62 | //main3()
63 | fun main3() = runBlocking {
64 | println("\n\n\n\n------ next program ")
65 | println("------ 100s of coroutines")
66 |
67 | // these will "wait" on the next one
68 | launch {
69 | println("\n\n")
70 |
71 | repeat(1_00) {
72 | print("$it ")
73 | delay(150L)
74 | }
75 | }
76 |
77 | // these will all run in "parallel"
78 | repeat(1_00) {
79 | println("\n\n")
80 | launch {
81 | delay(100L)
82 | print("-${it}-")
83 | }
84 | }
85 |
86 | }
87 |
88 | suspend fun doOne(): Int {
89 | delay(500L)
90 | return 13
91 | }
92 |
93 | suspend fun doTwo(): Int {
94 | delay(1000L)
95 | return 29
96 | }
97 |
98 |
99 |
100 | main4()
101 |
102 | fun main4() = runBlocking {
103 | println("\n\n\n\n------ next program ")
104 | println("------ coroutines sequencing")
105 |
106 | launch {
107 | println("This is executed before the first delay <1> [${Thread.currentThread().name}]")
108 | println("This is executed before the first delay <2> [${Thread.currentThread().name}]")
109 | stallForTime()
110 | println("This is executed after the first delay [${Thread.currentThread().name}]")
111 | }
112 |
113 | launch {
114 | println("This is executed before the second delay [${Thread.currentThread().name}]")
115 | stallForTime()
116 | println("This is executed after the second delay [${Thread.currentThread().name}]")
117 | }
118 |
119 | println("This is executed immediately [${Thread.currentThread().name}]")
120 | }
121 |
122 | suspend fun stallForTime() {
123 | println("before delay [${Thread.currentThread().name}]")
124 | // withContext(Dispatchers.IO) {
125 | // println("delaying on [${Thread.currentThread().name}]")
126 | delay(4000L)
127 | // }
128 | }
129 |
--------------------------------------------------------------------------------
/5.pluck-tags-from-blog-posts.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | /*
4 | *
5 | * This script goes through a specified folder
6 | * searches through all blog posts (markdown files)
7 | * plucks out the tags/categories from the front-matter of each post (should be in the "tags: []" format)
8 | * then prints out a list of all the tags/categories
9 | *
10 | * run this script like so:
11 | $> brew install kotlin
12 | $> kotlin 5.pluck-tags-from-blog-posts.main.kts ~/kau.sh/content/blog tags
13 | $> kotlin 5.pluck-tags-from-blog-posts.main.kts ~/kau.sh/content/blog categories
14 | *
15 | * I live coded another version of this script using Github Copilot:
16 | * You can read more [about it here](https://kau.sh/kscript-copilot/)
17 | * or watch the [youtube video here](https://www.youtube.com/watch?v=Tr2YBmecdw4)
18 | */
19 |
20 | import java.io.File
21 |
22 |
23 | println("******* PROGRAM START ***************** ")
24 | program(args)
25 | println("******* PROGRAM END ***************** ")
26 |
27 | fun program(args: Array) {
28 |
29 | if (args.isEmpty()) {
30 | println(" ⚠️ ERROR: a path is required ")
31 | println(" You need to provide two arguments to this script")
32 | println(" 1. path")
33 | println(" 2. tags, categories or duplicates")
34 | println(" duplicates will print out tags that are also categories")
35 | return
36 | }
37 |
38 | // return if we can't find atleast one .md file
39 | // also check any subdirectories
40 | val files = File(args[0]).walkTopDown().filter { file -> file.name.endsWith(".md") }.toList()
41 | if (files.isEmpty()) {
42 | println(" ⚠️ ERROR: no markdown files found ")
43 | return
44 | }
45 |
46 | println(" ℹ️ found ${files.size} markdown files to process")
47 |
48 | // return if second argument is not present
49 | if (args.size < 2) {
50 | println(" ⚠️ ERROR: second argument is required")
51 | return
52 | }
53 |
54 | val items: List = when (args[1]) {
55 | "tags", "categories" -> {
56 | collectTagsOrCategories(files, args[1])
57 | }
58 |
59 | "duplicates", "duplicate" -> {
60 | findDuplicates(files)
61 | }
62 |
63 | else -> {
64 | println(" ⚠️ ERROR: second argument must be either 'tags', 'categories' or 'duplicate(s)'")
65 | return
66 | }
67 | }
68 |
69 | justifiedPrint(items)
70 | }
71 |
72 | fun collectTagsOrCategories(files: List, tagOrCategorySearch: String): List {
73 | // read every line of file and pluck the line that has "tags: [" in it
74 | return files
75 | .map { file ->
76 | file.readLines()
77 | .filter { it.contains("${tagOrCategorySearch}: [") }
78 | .map { it.replace("${tagOrCategorySearch}: [", "").replace("]", "") }
79 | .map { it.split(",") }
80 | .flatten()
81 | .map { it.trim() }
82 | .filter { it.isNotEmpty() }
83 | }
84 | .flatten()
85 | .distinct()
86 | .sorted()
87 | }
88 |
89 | // This function collects all categories and tags from all files
90 | // then prints out the words that are both categories and tags
91 | fun findDuplicates(files: List): List {
92 | val tags = collectTagsOrCategories(files, "tags")
93 | val categories = collectTagsOrCategories(files, "categories")
94 | return tags.filter { categories.contains(it) }
95 | }
96 |
97 | /**
98 | * This function takes a list of strings and
99 | * prints them out in a justified format
100 | */
101 | fun justifiedPrint(words: List) {
102 | if(words.isEmpty()) {
103 | println(" \uD83E\uDEB9️ nothing to print")
104 | return
105 | }
106 |
107 | val maxWordLength = words.maxBy { it.length }.length ?: 0
108 | val maxWordsPerLine = 80 / (maxWordLength + 2)
109 |
110 | var line = ""
111 | words.forEach { word ->
112 | if (line.length + word.length + 2 > 80) {
113 | println(line)
114 | line = ""
115 | }
116 | line += word.padEnd(maxWordLength + 2)
117 | }
118 | println(line)
119 | }
120 |
--------------------------------------------------------------------------------
/6.tootbot.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | println("moved to a separate repo - https://github.com/kaushikgopal/tootbot")
4 |
--------------------------------------------------------------------------------
/7.carrot-wars-tabulate.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:DependsOn("com.squareup.okio:okio:3.3.0")
4 |
5 | import okio.FileSystem
6 | import okio.IOException
7 | import okio.Path
8 | import okio.Path.Companion.toPath
9 | import java.io.File
10 |
11 | /*
12 | * ---------------- Carrot Wars 2018 -----------------------------------
13 | * This script was used in Instacart's 2018 Summer Hackathon
14 | * it tabulates the results from a CSV file populated by Google Forms
15 | * and outputs the results of votes
16 | *
17 | * See the [accompanying blog post](https://tech.instacart.com/free-hackathon-vote-tabulation-using-google-forms-kotlin-3c7b7080ea) for more details.
18 | *
19 | * run this script like so:
20 |
21 | $> brew install kotlin
22 | $> kotlin 7.carrot-wars-tabulate.main.kts star-wars-demo-results.csv
23 | */
24 |
25 |
26 | println("******* PROGRAM START ***************** ")
27 | program(args)
28 | println("******* PROGRAM END ***************** ")
29 |
30 | fun program(args: Array) {
31 | if (checkValidCsvFile(args)) return
32 |
33 | var voterCount: Int = 0
34 | var projectCsv: Map = emptyMap()
35 | var projectCsvWithResults: Map = emptyMap()
36 |
37 | val filePath = args[0].toPath()
38 |
39 | processLinesFromFile(filePath) { line, index ->
40 | line ?: return@processLinesFromFile
41 |
42 | if (index == 0) {
43 | projectCsv = processHeaderAndBuildMap(line)
44 | } else {
45 | projectCsvWithResults = processVote(projectCsv, line)
46 | voterCount += 1
47 | }
48 | }
49 |
50 | println("$voterCount folk(s) voted!")
51 |
52 | printResults(projectCsvWithResults)
53 | }
54 | // ---------------------------------------------------------------------------------------------
55 | // Internal Helpers
56 |
57 | fun printResults(projectCsvColumn: Map) {
58 |
59 | val categoryComparator: Comparator =
60 | compareBy { it.project.category }
61 | val categoryAndMaxPointsComparator: Comparator =
62 | categoryComparator.thenByDescending { it.points }
63 |
64 | val orderedResults = projectCsvColumn.values
65 | .filter { it.points > 0 }
66 | .sortedWith(categoryAndMaxPointsComparator)
67 |
68 | orderedResults
69 | .groupBy { it.project.category }
70 | .forEach { map: Map.Entry> ->
71 | println("-----------------------\nCategory: ${map.key}\n----")
72 | map.value.forEach { println(it) }
73 | }
74 | }
75 |
76 |
77 | /**
78 | * A typical row response from Google Forms looks like this:
79 | *
80 | * (HEADER) "Timestamp","Username","Best Team Collaboration [1. Project 1]","Best Team Collaboration [2. Project 2]","Best Team Collaboration [3. Project 3]","Best Team Collaboration [4. Project 4]","Best Team Collaboration [5. Project 5]","Best Team Collaboration [6. Project 6]","Best Team Collaboration [7. Project 7]","Best Team Collaboration [8. Project 8]","Best Team Collaboration [9. Project 9]","Best Team Collaboration [10. Project 10]","Best Team Collaboration [11. Project 11]","Best Team Collaboration [12. Project 12]","Best Team Collaboration [13. Project 13]","Best Team Collaboration [14. Project 14]","Best Team Collaboration [15. Project 15]","Best Team Collaboration [16. Project 16]","Best Team Collaboration [17. Project 17]","Best Team Collaboration [18. Project 18]","Best Team Collaboration [19. Project 19]","Best Team Collaboration [20. Project 20]","Best Team Collaboration [21. Project 21]","Best Team Collaboration [22. Project 22]","Best Team Collaboration [23. Project 23]","Best Team Collaboration [24. Project 24]","Best Team Collaboration [25. Project 25]","Fixed most annoying longstanding issue [1. Project 1]","Fixed most annoying longstanding issue [2. Project 2]","Fixed most annoying longstanding issue [3. Project 3]","Fixed most annoying longstanding issue [4. Project 4]","Fixed most annoying longstanding issue [5. Project 5]","Fixed most annoying longstanding issue [6. Project 6]","Fixed most annoying longstanding issue [7. Project 7]","Fixed most annoying longstanding issue [8. Project 8]","Fixed most annoying longstanding issue [9. Project 9]","Fixed most annoying longstanding issue [10. Project 10]","Fixed most annoying longstanding issue [11. Project 11]","Fixed most annoying longstanding issue [12. Project 12]","Fixed most annoying longstanding issue [13. Project 13]","Fixed most annoying longstanding issue [14. Project 14]","Fixed most annoying longstanding issue [15. Project 15]","Fixed most annoying longstanding issue [16. Project 16]","Fixed most annoying longstanding issue [17. Project 17]","Fixed most annoying longstanding issue [18. Project 18]","Fixed most annoying longstanding issue [19. Project 19]","Fixed most annoying longstanding issue [20. Project 20]","Fixed most annoying longstanding issue [21. Project 21]","Fixed most annoying longstanding issue [22. Project 22]","Fixed most annoying longstanding issue [23. Project 23]","Fixed most annoying longstanding issue [24. Project 24]","Fixed most annoying longstanding issue [25. Project 25]","Greatest business impact [1. Project 1]","Greatest business impact [2. Project 2]","Greatest business impact [3. Project 3]","Greatest business impact [4. Project 4]","Greatest business impact [5. Project 5]","Greatest business impact [6. Project 6]","Greatest business impact [7. Project 7]","Greatest business impact [8. Project 8]","Greatest business impact [9. Project 9]","Greatest business impact [10. Project 10]","Greatest business impact [11. Project 11]","Greatest business impact [12. Project 12]","Greatest business impact [13. Project 13]","Greatest business impact [14. Project 14]","Greatest business impact [15. Project 15]","Greatest business impact [16. Project 16]","Greatest business impact [17. Project 17]","Greatest business impact [18. Project 18]","Greatest business impact [19. Project 19]","Greatest business impact [20. Project 20]","Greatest business impact [21. Project 21]","Greatest business impact [22. Project 22]","Greatest business impact [23. Project 23]","Greatest business impact [24. Project 24]","Greatest business impact [25. Project 25]","Saves the most time [1. Project 1]","Saves the most time [2. Project 2]","Saves the most time [3. Project 3]","Saves the most time [4. Project 4]","Saves the most time [5. Project 5]","Saves the most time [6. Project 6]","Saves the most time [7. Project 7]","Saves the most time [8. Project 8]","Saves the most time [9. Project 9]","Saves the most time [10. Project 10]","Saves the most time [11. Project 11]","Saves the most time [12. Project 12]","Saves the most time [13. Project 13]","Saves the most time [14. Project 14]","Saves the most time [15. Project 15]","Saves the most time [16. Project 16]","Saves the most time [17. Project 17]","Saves the most time [18. Project 18]","Saves the most time [19. Project 19]","Saves the most time [20. Project 20]","Saves the most time [21. Project 21]","Saves the most time [22. Project 22]","Saves the most time [23. Project 23]","Saves the most time [24. Project 24]","Saves the most time [25. Project 25]","Most innovative solution [1. Project 1]","Most innovative solution [2. Project 2]","Most innovative solution [3. Project 3]","Most innovative solution [4. Project 4]","Most innovative solution [5. Project 5]","Most innovative solution [6. Project 6]","Most innovative solution [7. Project 7]","Most innovative solution [8. Project 8]","Most innovative solution [9. Project 9]","Most innovative solution [10. Project 10]","Most innovative solution [11. Project 11]","Most innovative solution [12. Project 12]","Most innovative solution [13. Project 13]","Most innovative solution [14. Project 14]","Most innovative solution [15. Project 15]","Most innovative solution [16. Project 16]","Most innovative solution [17. Project 17]","Most innovative solution [18. Project 18]","Most innovative solution [19. Project 19]","Most innovative solution [20. Project 20]","Most innovative solution [21. Project 21]","Most innovative solution [22. Project 22]","Most innovative solution [23. Project 23]","Most innovative solution [24. Project 24]","Most innovative solution [25. Project 25]"
81 | * (ROW 1) "2018/06/28 10:29:47 AM MDT","hackathon@hotmail.com","3","","","","","","","","2","","","","1","","","","","","","","","","","","","","","","","","","","","","","","","","","","","2","","1","","3","","","","","","","","","","1","","2","","","","","","","","3","","","","","","","","","","3","","","","","","","","","2","","1","","","","","","","","","","","","","","1","2","","","","","3","","","","","","","","","","","","","","","","","",""
82 | *
83 | * We go through each row
84 | * From the map position, we query the project info
85 | * We update the points standing based on the vote value (weighted)
86 | *
87 | * return a new map that has project info + summary of all points
88 | */
89 | fun processVote(
90 | tableOfResults: Map, vote: String
91 | ): Map {
92 |
93 | val projectVoteResults: Map = tableOfResults
94 | val splitVote: List = vote.split(",")
95 | splitVote
96 | .drop(2) // not doing anything with the date
97 | .forEachIndexed loop@{ index, voteResultColumn ->
98 |
99 | val voteResultWithoutQuote = voteResultColumn.removeSurrounding("\"")
100 | if (voteResultWithoutQuote.isBlank()) return@loop
101 | val voteResult: Int = voteResultWithoutQuote.toInt()
102 |
103 | val position = index + 2 // we dropped 2 indices
104 |
105 | val projectVoteResult: HProjectVoteResult = projectVoteResults[position]
106 | ?: return@loop
107 |
108 | val project: HProject = projectVoteResult.project
109 | val voter: String = splitVote[1].removeSurrounding("\"")
110 |
111 | projectVoteResult.points += voteResult.voteWeight()
112 |
113 | projectVoteResult.voters.add(voter)
114 | projectVoteResult.votes.add(voteResult)
115 | // println("project '${project.name}' was voted '$voteResult' [${voteResult.voteWeight()}] in category '${project.category}' by '$voter'")
116 | }
117 |
118 | return projectVoteResults
119 | }
120 |
121 | /**
122 | * Process the header alone and initialize with 0 points everywhere
123 | * Google forms generates all combinations of entries
124 | * and creates a header column for all
125 | *
126 | * A typical header form the Google Forms response looks like this:
127 | *
128 | * (HEADER) "Timestamp","Username","Best Team Collaboration [1. Project 1]","Best Team Collaboration [2. Project 2]","Best Team Collaboration [3. Project 3]","Best Team Collaboration [4. Project 4]","Best Team Collaboration [5. Project 5]","Best Team Collaboration [6. Project 6]","Best Team Collaboration [7. Project 7]","Best Team Collaboration [8. Project 8]","Best Team Collaboration [9. Project 9]","Best Team Collaboration [10. Project 10]","Best Team Collaboration [11. Project 11]","Best Team Collaboration [12. Project 12]","Best Team Collaboration [13. Project 13]","Best Team Collaboration [14. Project 14]","Best Team Collaboration [15. Project 15]","Best Team Collaboration [16. Project 16]","Best Team Collaboration [17. Project 17]","Best Team Collaboration [18. Project 18]","Best Team Collaboration [19. Project 19]","Best Team Collaboration [20. Project 20]","Best Team Collaboration [21. Project 21]","Best Team Collaboration [22. Project 22]","Best Team Collaboration [23. Project 23]","Best Team Collaboration [24. Project 24]","Best Team Collaboration [25. Project 25]","Fixed most annoying longstanding issue [1. Project 1]","Fixed most annoying longstanding issue [2. Project 2]","Fixed most annoying longstanding issue [3. Project 3]","Fixed most annoying longstanding issue [4. Project 4]","Fixed most annoying longstanding issue [5. Project 5]","Fixed most annoying longstanding issue [6. Project 6]","Fixed most annoying longstanding issue [7. Project 7]","Fixed most annoying longstanding issue [8. Project 8]","Fixed most annoying longstanding issue [9. Project 9]","Fixed most annoying longstanding issue [10. Project 10]","Fixed most annoying longstanding issue [11. Project 11]","Fixed most annoying longstanding issue [12. Project 12]","Fixed most annoying longstanding issue [13. Project 13]","Fixed most annoying longstanding issue [14. Project 14]","Fixed most annoying longstanding issue [15. Project 15]","Fixed most annoying longstanding issue [16. Project 16]","Fixed most annoying longstanding issue [17. Project 17]","Fixed most annoying longstanding issue [18. Project 18]","Fixed most annoying longstanding issue [19. Project 19]","Fixed most annoying longstanding issue [20. Project 20]","Fixed most annoying longstanding issue [21. Project 21]","Fixed most annoying longstanding issue [22. Project 22]","Fixed most annoying longstanding issue [23. Project 23]","Fixed most annoying longstanding issue [24. Project 24]","Fixed most annoying longstanding issue [25. Project 25]","Greatest business impact [1. Project 1]","Greatest business impact [2. Project 2]","Greatest business impact [3. Project 3]","Greatest business impact [4. Project 4]","Greatest business impact [5. Project 5]","Greatest business impact [6. Project 6]","Greatest business impact [7. Project 7]","Greatest business impact [8. Project 8]","Greatest business impact [9. Project 9]","Greatest business impact [10. Project 10]","Greatest business impact [11. Project 11]","Greatest business impact [12. Project 12]","Greatest business impact [13. Project 13]","Greatest business impact [14. Project 14]","Greatest business impact [15. Project 15]","Greatest business impact [16. Project 16]","Greatest business impact [17. Project 17]","Greatest business impact [18. Project 18]","Greatest business impact [19. Project 19]","Greatest business impact [20. Project 20]","Greatest business impact [21. Project 21]","Greatest business impact [22. Project 22]","Greatest business impact [23. Project 23]","Greatest business impact [24. Project 24]","Greatest business impact [25. Project 25]","Saves the most time [1. Project 1]","Saves the most time [2. Project 2]","Saves the most time [3. Project 3]","Saves the most time [4. Project 4]","Saves the most time [5. Project 5]","Saves the most time [6. Project 6]","Saves the most time [7. Project 7]","Saves the most time [8. Project 8]","Saves the most time [9. Project 9]","Saves the most time [10. Project 10]","Saves the most time [11. Project 11]","Saves the most time [12. Project 12]","Saves the most time [13. Project 13]","Saves the most time [14. Project 14]","Saves the most time [15. Project 15]","Saves the most time [16. Project 16]","Saves the most time [17. Project 17]","Saves the most time [18. Project 18]","Saves the most time [19. Project 19]","Saves the most time [20. Project 20]","Saves the most time [21. Project 21]","Saves the most time [22. Project 22]","Saves the most time [23. Project 23]","Saves the most time [24. Project 24]","Saves the most time [25. Project 25]","Most innovative solution [1. Project 1]","Most innovative solution [2. Project 2]","Most innovative solution [3. Project 3]","Most innovative solution [4. Project 4]","Most innovative solution [5. Project 5]","Most innovative solution [6. Project 6]","Most innovative solution [7. Project 7]","Most innovative solution [8. Project 8]","Most innovative solution [9. Project 9]","Most innovative solution [10. Project 10]","Most innovative solution [11. Project 11]","Most innovative solution [12. Project 12]","Most innovative solution [13. Project 13]","Most innovative solution [14. Project 14]","Most innovative solution [15. Project 15]","Most innovative solution [16. Project 16]","Most innovative solution [17. Project 17]","Most innovative solution [18. Project 18]","Most innovative solution [19. Project 19]","Most innovative solution [20. Project 20]","Most innovative solution [21. Project 21]","Most innovative solution [22. Project 22]","Most innovative solution [23. Project 23]","Most innovative solution [24. Project 24]","Most innovative solution [25. Project 25]"
129 | * (ROW 1) "2018/06/28 10:29:47 AM MDT","hackathon@hotmail.com","3","","","","","","","","2","","","","1","","","","","","","","","","","","","","","","","","","","","","","","","","","","","2","","1","","3","","","","","","","","","","1","","2","","","","","","","","3","","","","","","","","","","3","","","","","","","","","2","","1","","","","","","","","","","","","","","1","2","","","","","3","","","","","","","","","","","","","","","","","",""
130 | *
131 | * We try to parse the header and build an empty map with the
132 | * position as the map's key -> when going through each row column, we'd know which project the vote was for
133 | * value -> project vote result (basically project + category + current points standing)
134 | */
135 | fun processHeaderAndBuildMap(line: String): Map {
136 | var results: MutableMap = mutableMapOf()
137 |
138 | val splitHeader: List = line.split(",")
139 |
140 | /*
141 | println("---- Debugging Header processing")
142 | splitHeader
143 | .take(5)
144 | .forEachIndexed { index, value ->
145 | println("column ${value} is at ${index}")
146 | }
147 | */
148 |
149 | splitHeader
150 | .drop(2) // we don't want the username & timestamp just yet; so ignore columns
151 | .forEachIndexed { index, headerColumn ->
152 | // println("column name ${headerColumn} at position ${index + 2}")
153 | results[index + 2] = HProjectVoteResult(processProjectFromColumnHeader(headerColumn))
154 | // println("processed $projectResult")
155 | }
156 |
157 | return results
158 | }
159 |
160 | fun processProjectFromColumnHeader(headerColumn: String): HProject {
161 | val projectCategory: String = headerColumn.split("[")[0].trim()
162 | val projectName: String = headerColumn.let {
163 | it.substring(it.indexOf("[") + 1, it.indexOf("]")).trim()
164 | }
165 | return HProject(projectCategory, projectName)
166 | }
167 |
168 | fun Int.voteWeight(): Int {
169 | return when (this) {
170 | 1 -> 6
171 | 2 -> 3
172 | 3 -> 2
173 | else -> throw RuntimeException("there can be only 3")
174 | }
175 | }
176 |
177 | // ---------------------------------------------------------------------------------------------
178 | // Data structures
179 |
180 |
181 | /**
182 | * Hackathon project
183 | */
184 | data class HProject(
185 | val category: String,
186 | val name: String
187 | )
188 |
189 | /**
190 | * Hackathon project result row
191 | */
192 | data class HProjectVoteResult(
193 | val project: HProject,
194 | var points: Int = 0,
195 | var voters: MutableList = arrayListOf(),
196 | var votes: MutableList = arrayListOf()
197 | ) {
198 | override fun toString(): String {
199 | return "${project.name.padEnd(50)} ----> ${points} <- ${votes}"
200 | }
201 | }
202 |
203 | fun checkValidCsvFile(args: Array): Boolean {
204 | if (args.isEmpty()) {
205 | println("⚠️ We need a csv file to proceed!")
206 | return true
207 | }
208 |
209 | // make sure one argument provided and it is a csv file
210 | val file = File(args[0])
211 |
212 | if (!file.exists()) {
213 | println("⚠️ File ${file.absolutePath} does not exist!")
214 | return true
215 | }
216 |
217 | if (!file.isFile) {
218 | println("⚠️ File ${file.absolutePath} is not a file!")
219 | return true
220 | }
221 | if (!file.canRead()) {
222 | println("⚠️ File ${file.absolutePath} is not readable!")
223 | return true
224 | }
225 | if (!file.extension.equals("csv", ignoreCase = true)) {
226 | println("⚠️ File ${file.absolutePath} is not a CSV file!")
227 | return true
228 | }
229 | return false
230 | }
231 |
232 | @Throws(IOException::class)
233 | fun processLinesFromFile(path: Path, lineAction: (String?, Int) -> Unit) {
234 | FileSystem.SYSTEM.read(path) {
235 | var lineCount = 0
236 | while (true) {
237 | val line = readUtf8Line() ?: break
238 | lineAction.invoke(line, lineCount)
239 | lineCount += 1
240 | }
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/8.embed-external-scripts.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:Import("common.main.kts")
4 |
5 | import java.io.File
6 |
7 | /*
8 | * This script demonstrates how you can import or embed external kts scripts
9 | * run this script like so:
10 |
11 | $> brew install kotlin
12 | $> kotlin 8.embed-external-scripts.main.kts
13 | *
14 | * alternatively if you want to make the script executable
15 |
16 | $> chmod +x 8.embed-external-scripts.main.kts
17 | $> ./8.embed-external-scripts.main.kts
18 | */
19 |
20 | println("******* PROGRAM START ***************** ")
21 | program(args)
22 | println("******* PROGRAM END ***************** ")
23 |
24 | fun program(args : Array) {
25 | val person = Person(name = "Albert Einstein", age = 76)
26 | println("printing from class MyClassObject - ${person.name} is ${person.age} years old")
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/9.curl-vs-okhttp-api-system-env-json-parse-gitlab.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:Repository("https://repo.maven.apache.org/maven2/") @file:DependsOn("com.squareup.okhttp3:okhttp:4.10.0") @file:DependsOn(
4 | "com.squareup.okio:okio:3.9.0",
5 | ) @file:DependsOn("com.squareup.moshi:moshi-kotlin:1.15.1") @file:DependsOn("com.squareup.moshi:moshi-adapters:1.13.0") @file:DependsOn(
6 | "dev.zacsweers.moshix:moshi-metadata-reflect:0.27.1",
7 | )
8 |
9 | @file:Repository("https://jitpack.io") @file:DependsOn("com.github.kaushikgopal:shell.main.kts:276950a346")
10 |
11 | import com.squareup.moshi.Json
12 | import com.squareup.moshi.Moshi
13 | import com.squareup.moshi.Types
14 | import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
15 | import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory
16 | import okhttp3.OkHttpClient
17 | import okhttp3.Request
18 | import okio.buffer
19 | import java.util.*
20 | import okio.source
21 | import java.io.File
22 | import java.io.IOException
23 |
24 | val ANSI_RESET = "\u001B[0m"
25 | val ANSI_GRAY = "\u001B[90m" // use this mostly
26 | val ANSI_PURPLE = "\u001B[35m" // input commands
27 | val ANSI_GREEN = "\u001B[32m" // highlighting values
28 | val ANSI_RED = "\u001B[31m" // error
29 | val ANSI_YELLOW = "\u001B[33m" // important messages
30 | //val ANSI_BLUE = "\u001B[34m"
31 | //val ANSI_CYAN = "\u001B[36m"
32 | //val ANSI_WHITE = "\u001B[37m"
33 |
34 | val DEBUG = true
35 | val gitlabApiToken =
36 | System.getenv("GITLAB_API_TOKEN") ?: throw IllegalStateException("PRIVATE_TOKEN must be set")
37 |
38 | val moshi: Moshi = Moshi.Builder().add(MetadataKotlinJsonAdapterFactory())
39 | .add(Date::class.java, Rfc3339DateJsonAdapter()).build()
40 |
41 | val client = OkHttpClient()
42 |
43 | println("$ANSI_GRAY----$ANSI_RESET")
44 | program(args)
45 | println("$ANSI_GRAY----$ANSI_RESET")
46 |
47 | fun program(args: Array) {
48 | if (DEBUG) println("$ANSI_GRAY[args]$ANSI_GREEN${args.joinToString()}$ANSI_RESET")
49 |
50 | getProjects().forEach { project ->
51 | println(
52 | """
53 | $ANSI_GRAY[${project.id}]$ANSI_GREEN ${project.name}
54 | """.trimMargin(),
55 | )
56 | }
57 |
58 | getMergeRequests().forEach { mr ->
59 | println(
60 | """
61 | $ANSI_GRAY[$ANSI_YELLOW${mr.id}$ANSI_GRAY]: ${ANSI_GREEN}${mr.title} - ${ANSI_GREEN}${mr.author.name}
62 | """.trimMargin(),
63 | )
64 | }
65 | }
66 |
67 | private fun getProjects(): List {
68 |
69 | val listType = Types.newParameterizedType(List::class.java, GitlabProject::class.java)
70 | val adapter = moshi.adapter>(listType)
71 |
72 | // Command to execute the API call
73 | val command = listOf(
74 | "curl",
75 | "-H", "PRIVATE-TOKEN: $gitlabApiToken",
76 | "https://gitlab.com/api/v4/groups/caperai/projects",
77 | )
78 |
79 | // Temporarily store output in a file
80 | val tempFile = File.createTempFile("response", ".json")
81 |
82 | val projects = try {
83 | val process =
84 | ProcessBuilder(command).redirectOutput(ProcessBuilder.Redirect.to(tempFile)).start()
85 | process.waitFor()
86 |
87 | // Read the output
88 | val source = tempFile.source().buffer()
89 | val jsonOutput = source.readUtf8()
90 | source.close()
91 |
92 | // Parse JSON to Kotlin object
93 | adapter.fromJson(jsonOutput) ?: emptyList()
94 |
95 | } catch (e: IOException) {
96 | e.printStackTrace()
97 | emptyList()
98 | } finally {
99 | // Clean up the temporary file
100 | tempFile.delete()
101 | }
102 |
103 | return projects
104 | }
105 |
106 | private fun getMergeRequests(): List {
107 | val listType = Types.newParameterizedType(List::class.java, MergeRequest::class.java)
108 | val adapter = moshi.adapter>(listType)
109 |
110 | // Build the request
111 | val request = Request.Builder()
112 | .url("https://gitlab.com/api/v4/projects/40196776/merge_requests?state=opened")
113 | .addHeader("PRIVATE-TOKEN", gitlabApiToken).build()
114 |
115 | return try {
116 | // Execute the request
117 | val response = client.newCall(request).execute()
118 | response.use {
119 | if (!it.isSuccessful) throw IOException("[${it.code}] ${it.body?.string()}")
120 | val jsonOutput = it.body!!.source()
121 | adapter.fromJson(jsonOutput) ?: emptyList()
122 | }
123 | } catch (e: IOException) {
124 | e.printStackTrace()
125 | emptyList()
126 | }
127 | }
128 |
129 | data class GitlabProject(val name: String, val id: Int)
130 |
131 | data class MergeRequest(
132 | val id: Int,
133 | val title: String,
134 | @Json(name = "created_at") val created: Date,
135 | @Json(name = "web_url") val webUrl: String,
136 | val author: User,
137 | val assignees: List,
138 | val assignee: User?,
139 | val reviewers: List,
140 | )
141 |
142 | class User(
143 | val id: Int,
144 | val username: String,
145 | val name: String,
146 | )
147 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # setup
2 |
3 | Each script should have instructions + the command on how to run it.
4 | But in general you'll need to install kotlin like so:
5 |
6 | ```sh
7 | # install sdkman
8 | # i like sdkman cause it allows us to install different versions
9 | curl -s "https://get.sdkman.io" | bash
10 | # install kotlin (1.9.24 until 2.0 starts working well with the libs used)
11 | sdk install kotlin 1.9.24
12 |
13 | # run the script
14 | kotlin 0.hello.main.kts Hello 🌎
15 | ```
16 |
17 | Alternatively, if you want to make the script executable
18 |
19 | ```sh
20 | chmod +x 0.hello.main.kts
21 |
22 | # you can now run the script like so:
23 | ./0.hello.main.kts Hello 🌎
24 | ```
25 |
26 | _Note: The first time you run the script it'll look like it's taking forever, thereafter the script runs super fast_.
27 |
--------------------------------------------------------------------------------
/common.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | data class Person(
4 | val name: String,
5 | val age: Int
6 | )
7 |
--------------------------------------------------------------------------------
/star-wars-demo-results.csv:
--------------------------------------------------------------------------------
1 | "Timestamp","Which was your favorite SW movie from the original episodes? [STAR WARS: EPISODE VIII THE LAST JEDI]","Which was your favorite SW movie from the original episodes? [STAR WARS: EPISODE VII THE FORCE AWAKENS]","Which was your favorite SW movie from the original episodes? [STAR WARS: EPISODE VI RETURN OF THE JEDI]","Which was your favorite SW movie from the original episodes? [STAR WARS: EPISODE V THE EMPIRE STRIKES BACK]","Which was your favorite SW movie from the original episodes? [STAR WARS: EPISODE IV A NEW HOPE]","Which was your favorite SW movie from the original episodes? [STAR WARS: EPISODE III REVENGE OF THE SITH]","Which was your favorite SW movie from the original episodes? [STAR WARS: EPISODE II ATTACK OF THE CLONES]","Which was your favorite SW movie from the original episodes? [STAR WARS: EPISODE I THE PHANTOM MENACE]","Which was your favorite standalone SW movie? [SOLO: A STAR WARS STORY]","Which was your favorite standalone SW movie? [ROGUE ONE: A STAR WARS STORY]","Which is your favorite star wars movie overall? [SOLO: A STAR WARS STORY]","Which is your favorite star wars movie overall? [STAR WARS: EPISODE VIII THE LAST JEDI]","Which is your favorite star wars movie overall? [ROGUE ONE: A STAR WARS STORY]","Which is your favorite star wars movie overall? [STAR WARS: EPISODE VII THE FORCE AWAKENS]","Which is your favorite star wars movie overall? [STAR WARS: EPISODE VI RETURN OF THE JEDI]","Which is your favorite star wars movie overall? [STAR WARS: EPISODE V THE EMPIRE STRIKES BACK]","Which is your favorite star wars movie overall? [STAR WARS: EPISODE IV A NEW HOPE]","Which is your favorite star wars movie overall? [STAR WARS: EPISODE III REVENGE OF THE SITH]","Which is your favorite star wars movie overall? [STAR WARS: EPISODE II ATTACK OF THE CLONES]","Which is your favorite star wars movie overall? [STAR WARS: EPISODE I THE PHANTOM MENACE]"
2 | "2018/09/10 10:34:21 AM GMT+3","","2","","3","1","","","","2","1","","","3","2","","","1","","",""
3 | "2018/09/11 6:11:47 AM GMT+3","","","3","1","2","","","","2","1","","","","","3","1","2","","",""
--------------------------------------------------------------------------------
/toots.csv:
--------------------------------------------------------------------------------
1 | blog/2023-02-03-toot-bot-post-mastodon-automatically-kotlin-script/index.md,109804252811332111
2 |
--------------------------------------------------------------------------------
/x.test.main.kts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env kotlin
2 |
3 | @file:Repository("https://repo.maven.apache.org/maven2/")
4 | @file:DependsOn("org.jetbrains.kotlin:kotlin-test:1.3.72")
5 |
6 | import kotlin.test.assertEquals
7 |
8 | /*
9 | * This script is a template for quickly running tests
10 | *
11 | * To run kscripts do it like so:
12 | $> brew install kotlin
13 | $> chmod +x x.test.main.kts
14 | $> ./x.test.main.kts
15 | */
16 |
17 |
18 | fun isEditDistance1(string1: String, string2: String): Boolean {
19 | return false
20 | }
21 |
22 | assertEquals(true, isEditDistance1("cat", "cat"), "\uD83D\uDEA8")
23 |
24 | assertEquals(true, isEditDistance1("cat", "cat"), "🚨")
25 | assertEquals(false, isEditDistance1("cat", "dog"), "🚨")
26 | assertEquals(true, isEditDistance1("cat", "mat"), "🚨")
27 | assertEquals(true, isEditDistance1("cat", "chat"), "🚨")
28 | assertEquals(false, isEditDistance1("cat", "act"), "🚨")
29 | assertEquals(false, isEditDistance1("cat", "palindrome"), "🚨")
30 | assertEquals(true, isEditDistance1("cat", "cats"), "🚨")
31 |
32 |
33 | fun isEditDistance(maxDifference: Int, string1: String, string2: String): Boolean {
34 | return false
35 | }
36 |
37 | assertEquals(true, isEditDistance(0, "cat", "cat"), "\uD83D\uDEA8")
38 | assertEquals(true, isEditDistance(0, "cat", "cat"), "🚨")
39 | assertEquals(false, isEditDistance(1, "cat", "dog"), "🚨")
40 | assertEquals(true, isEditDistance(1, "cat", "mat"), "🚨")
41 | assertEquals(true, isEditDistance(1, "cat", "chat"), "🚨")
42 | assertEquals(true, isEditDistance(2, "ban", "bhane"), "🚨")
43 | assertEquals(false, isEditDistance(2, "pal", "palindrome"), "🚨")
44 | assertEquals(false, isEditDistance(1, "cat", "act"), "🚨")
45 | assertEquals(true, isEditDistance(1, "cats", "cat"), "🚨")
46 | assertEquals(true, isEditDistance(2, "cat", "chats"), "🚨")
47 | assertEquals(true, isEditDistance(3, "sitting", "kitten"), "🚨")
48 | assertEquals(false, isEditDistance(2, "sitting", "kitten"), "🚨")
49 |
50 |
--------------------------------------------------------------------------------