A Scala implementation of cowsay, written from scratch, but of course inspired by the original cowsay program written by Tony Monroe.
_______________
< Cows ♥ Scala! >
---------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
See also:
- Wikipedia for more info on cowsay
- tnalpgge/rank-amateur-cowsay for the original program
A web app is included in the module web
, because we all know that of course
everything is better in the Cloud ☁️. Written with Akka HTTP.
It aims to provide, in a single app:
- a webpage to manually generate cowsay outputs
- a RESTful API providing the same service
- Slack integration
Cowsay-online is accessible at cowsay-online.herokuapp.com.
A command line app is included in the module cli
. Run it with --help
for
usage information.
Cowsay4s is made up of three libraries:
cowsay4s-core
(which implements the Cowsay mechanics)cowsay4s-defaults
(which provides a bunch of default "cows" and "modes" to choose from)cowsay4s-asciimojis
which adds support for "asciimojis" in the text messages displayed by cowsay4s
Dependencies when building with Mill:
ivy"fr.ggaly::cowsay4s-core:0.2.2" // or ivy"fr.ggaly::cowsay4s-core::0.2.2" for Scala.js
ivy"fr.ggaly::cowsay4s-defaults:0.2.2" // or ivy"fr.ggaly::cowsay4s-defaults::0.2.2" for Scala.js
ivy"fr.ggaly::cowsay4s-asciimojis:0.2.2" // or ivy"fr.ggaly::cowsay4s-asciimojis::0.2.2" for Scala.js
Dependencies when building with SBT:
"fr.ggaly" %% "cowsay4s-core" % "0.2.2" // or "fr.ggaly" %%% "cowsay4s-core" % "0.2.2" for Scala.js
"fr.ggaly" %% "cowsay4s-defaults" % "0.2.2" // or "fr.ggaly" %%% "cowsay4s-defaults" % "0.2.2" for Scala.js
"fr.ggaly" %% "cowsay4s-asciimojis" % "0.2.2" // or "fr.ggaly" %%% "cowsay4s-asciimojis" % "0.2.2" for Scala.js
Here is the example at the beginning of this README file, implemented
using only cowsay4s-core
and providing a custom "cow" (ascii art):
import cowsay4s.core._
val myCustomCow: Cow = CustomCow(
"""
| $thoughts ^__^
| $thoughts ($eyes)\\_______
| (__)\\ )\\/\\
| $tongue ||----w |
| || ||
""".stripMargin
)
val myCommand: CowCommand = CowCommand(
cow = myCustomCow,
message = "Cows ♥ Scala!",
eyes = CowEyes.default, // optional, defaults to 'CowEyes.default' (same as this example)
tongue = CowTongue.default, // optional, defaults to 'CowTongue.default' (same as this example)
action = CowAction.CowSay, // Optional, defaults to CowAction.defaultValue (same as this example)
wrap = MessageWrapping(40) // optional, defaults to 'MessageWrapping.default' (same as this example)
)
val result: String = CowSay.default.talk(myCommand)
println(result)
And here is the same example, using some of the included defaults:
import cowsay4s.core._
import cowsay4s.defaults.{DefaultCow, DefaultCowMode}
val myCommand: CowCommand = CowCommand(
cow = DefaultCow.Default, // There are many other cows to choose from in 'DefaultCow'
message = "Cows ♥ Scala!",
mode = DefaultCowMode.Default, // Optional, defaults to DefaultCowMode.defaultValue (same as this example)
action = CowAction.CowSay, // Optional, defaults to CowAction.defaultValue (same as this example)
wrap = MessageWrapping(40) // optional, defaults to 'MessageWrapping.default' (same as this example)
)
val result: String = CowSay.default.talk(myCommand)
println(result)
The action
determines wether the cow "says" or "thinks" the provided
message
(the speech bubble is formatted differently).
The cow
provides the drawing below the speech bubble.
The eyes
and tongue
values replace the $eyes
and $tongue
placeholders in the cow (when they exist; many cows lack those
placeholders). They can also be provided together as a single parameter,
called mode
.
wrap
determines the maximum width of the text before it gets wrapped
with line breaks. This works reasonably well with most unicode
characters on the JVM, less so if you use the Scala.js variant.
On the JVM platform only (not available on Scala.js), you can render a
cow as a bitmap image instead of plain text. You just need to
import cowsay4s.core.BitmapCows._
to add the necessary methods to an
instance of Cowsay
.
A note on performance: the first time your program renders a bitmap cow, it may be pretty slow, presumably because of some initialization being performed inside the Java graphics APIs. The following renders should be much faster. On my computer, a simple example can take about 1200 ms to render on the first iteration and about 15 to 20 ms on following iterations.
import java.awt.{Color, Font}
import java.awt.image.BufferedImage
import java.nio.file.Paths
import cowsay4s.core._
import cowsay4s.core.BitmapCows._
val cowsay = CowSay.default
val myCommand: CowCommand = ??? // Any CowCommand you want
val font = new Font("VT323", Font.PLAIN, 18)
val fontColor = Color.BLACK
// If you don't provide a background color, it will be kept transparent
val backgroundColor = Some(Color.WHITE)
// Output to a Java BufferedImage
val bufferedImage: BufferedImage =
cowsay.talkToBufferedImage(command, font, fontColor, backgroundColor)
// Output to a PNG (in memory)
val pngBytes: Array[Byte] =
cowsay.talkToPng(command, font, fontColor, backgroundColor)
// Output to a PNG file
val path = Paths.get("/tmp/cowsay.png")
cowsay.talkToPngFile(path, command, font, fontColor, backgroundColor)
The CowSay
implementation can be customized by the use of
CommandTransformer
s, which are juste functions to transform the
commands received by the CowSay
implementation.
Here's an example :
import cowsay4s.core._
val muteTransformer: CowSay.CommandTransformer =
(command: CowCommand) => command.copy(message = "")
val thinkingTransformer: CowSay.CommandTransformer =
(command: CowCommand) => command.copy(action = CowAction.CowThink)
// This CowSay will allways be thinking and mute, not matter what
// command you feed it.
val thinkingAndMuteCowSay =
CowSay.withTransformers(thinkingTransformer, muteTransformer)