<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ces-D</title>
    <description>The latest articles on DEV Community by Ces-D (@cesd).</description>
    <link>https://dev.to/cesd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F652729%2F077d3d2a-14b1-4106-aea9-8df547771337.jpeg</url>
      <title>DEV Community: Ces-D</title>
      <link>https://dev.to/cesd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cesd"/>
    <language>en</language>
    <item>
      <title>Starting a CLI Finance Tool</title>
      <dc:creator>Ces-D</dc:creator>
      <pubDate>Thu, 05 Aug 2021 22:04:13 +0000</pubDate>
      <link>https://dev.to/cesd/starting-a-cli-finance-tool-5acf</link>
      <guid>https://dev.to/cesd/starting-a-cli-finance-tool-5acf</guid>
      <description>&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;This is a finance tool project based heavily on &lt;a href="https://github.com/beancount/beancount"&gt;beancount&lt;/a&gt; but with less features and way less complexity. After reviewing the beancount repo and documentation, I noticed that there are many more features available that I will use. In an attempt to create a new project with a new language, C#, this project was born.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outline
&lt;/h2&gt;

&lt;p&gt;This is a console application. As of right now the only major dependency is the &lt;code&gt;System.CommandLine&lt;/code&gt; package. The commands code is written in &lt;code&gt;namespace Controllers.Commands&lt;/code&gt; and the parsing and formatting of entries is written in &lt;code&gt;namespace Controllers.VaultAccess&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commands
&lt;/h3&gt;

&lt;p&gt;Here is an example of one of my commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using Controllers.VaultAccess;
using Config;

namespace Controllers.Commands
{

    public class NewCommand : Command
    {

        public NewCommand() : base("new", "Create a new vault entry")
        {
            AddArgument(DetailsArgument());
            AddOption(DateOption());
            AddOption(DirectiveOption());
            AddOption(ValueOption());
            Handler = CommandHandler.Create((DateTime date, string directiveType, string details, double currency) =&amp;gt;
            {
                JournalEntry entry = new JournalEntry(date, directiveType, details, currency);
                Console.WriteLine(entry.ToString());
            });
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logic is unfinished but clear. My command class inherits from &lt;code&gt;System.CommandLine.Command&lt;/code&gt; and abstracts away all of its logic when initialized. Using this format I am able to encapsulate the Options and Arguments into their own private methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; private Argument&amp;lt;string&amp;gt; DetailsArgument()
        {
            Argument&amp;lt;string&amp;gt; details = new Argument&amp;lt;string&amp;gt;("details");
            details.Description = "The details of this journal entry";

            return details;
        }

        private Option&amp;lt;DateTime&amp;gt; DateOption()
        {
            Option&amp;lt;DateTime&amp;gt; date = new Option&amp;lt;DateTime&amp;gt;("--date");
            date.AddAlias("-d");
            date.Description = "Date of entry";
            date.SetDefaultValue(DateTime.Now);

            return date;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Journal Entry
&lt;/h3&gt;

&lt;p&gt;Beancount is a very mature program and they have tested and perfected the input format for their entries. I am matching their input formats and created the &lt;code&gt;JournalEntry&lt;/code&gt; class to handle the input, parsing, formatting, and output of the journal entry.&lt;br&gt;
Here is an example of the format that I am expecting to output into my journal files&lt;br&gt;
&lt;code&gt;&amp;lt;Date&amp;gt;   &amp;lt;Directive&amp;gt;      &amp;lt;Details&amp;gt;         &amp;lt;Value&amp;gt;&amp;lt;Currency&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;code&gt;08/05/2021 open    Liability:Food:Restaurant    -35 USD&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JournalEntry
    {
        private readonly int DATE_MAX_WIDTH = 13;
        private readonly int DIRECTIVE_MAX_WIDTH = 7;
        private readonly int DETAILS_MAX_WIDTH = 45;
        private DateTime entryDate { get; set; }
        private string directive { get; set; }
        private string details { get; set; }
        private double currency { get; set; }
        public JournalEntry(DateTime date, string dir, string det, double cur)
        {
            entryDate = date;
            directive = dir;
            details = det;
            currency = cur;
        }

        public override string ToString()
        {
            return string.Format("{0}{1}{2}{3}",
            entryDate.ToShortDateString().PadRight(DATE_MAX_WIDTH),
            directive.PadRight(DIRECTIVE_MAX_WIDTH),
            details.PadRight(DETAILS_MAX_WIDTH),
            currency);
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;The hardest feature that I have yet to implement are the accounting logic and maybe more importantly the parsing of each line in the journal to make the various accounting reports. I intend to add reports for all the open assets and liabilities, balances of accounts, and history of expenditures.&lt;br&gt;
The benefit of this project is that it is not the first. I can copy over some of the logic from existing accounting tools and then build and better them.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/Ces-D/cobblepot"&gt;Github Repo&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>csharp</category>
    </item>
    <item>
      <title>Discord Bot Framework</title>
      <dc:creator>Ces-D</dc:creator>
      <pubDate>Sun, 04 Jul 2021 21:50:16 +0000</pubDate>
      <link>https://dev.to/cesd/discord-bot-framework-21k0</link>
      <guid>https://dev.to/cesd/discord-bot-framework-21k0</guid>
      <description>&lt;p&gt;In this post I am going to be explaining the fully extensible discord bot built using &lt;a href="https://discord.js.org/#/"&gt;DiscordJs&lt;/a&gt;. I am going to skip the part where you connect the bot to a discord server because this is explained in many other discord related blog postings. I will be running through and explaining the core files of the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launcher
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const main = () =&amp;gt; {
  const prisma = new PrismaClient();
  const client = new Client();
  const control = new CommandControl(client, prisma);

  client.once(Constants.Events.CLIENT_READY, () =&amp;gt; {
    prisma
      .$connect()
      .then(() =&amp;gt; console.log("Prisma Connected"))
      .finally(() =&amp;gt; console.log("Application Ready"));
  });

  client.on(Constants.Events.DISCONNECT, () =&amp;gt; {
    prisma
      .$disconnect()
      .then(() =&amp;gt; console.log("Prisma Disconnected"))
      .finally(() =&amp;gt; console.log("Application Disconnected"));
  });

  client.on(Constants.Events.MESSAGE_CREATE, (message) =&amp;gt; {
    control.handleMessage(message);
  });

  client.login(process.env.Bot_Token);
};

main();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This project relies heavily on typescript to keep writing consistent. Important variables to note are the &lt;code&gt;client&lt;/code&gt; and &lt;code&gt;control&lt;/code&gt;. All commands come in as Messages and are handled when the message event is listened to. If you are unfamiliar with the &lt;code&gt;EventListener&lt;/code&gt; in node check out this &lt;a href="https://nodejs.org/api/events.html"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CommandControl
&lt;/h2&gt;

&lt;p&gt;This class delegates all commands to their respective handlers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; handleMessage(message: Message) {
    if (this.isAdmissableMessage(message)) {
      const command = this.parseForCommand(message);
      switch (command) {
        case UserEconomyCmds.LIST:
          this.Economy.listCommands(message);
          break;
        case UserEconomyCmds.CREATE_WALLET:
          this.Economy.createCommand(message);
          break;
        case UserEconomyCmds.BALANCE:
          this.Economy.balanceCommand(message);
          break;
        case UserEconomyCmds.MERCHANDISE:
          this.Economy.getMerchandiseCommand(message);
          break;
        case UserEconomyCmds.POSSESSIONS:
          this.Economy.getPossessionsCommand(message);
          break;
        case UserEconomyCmds.PURCHASE:
          break;
        case AdminEconomyCmds.SUMMARY:
          this.Economy.summaryWalletCommand(message);
          break;
        default:
          console.log(command);
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just an example so is already populated with optional commands. Firstly, &lt;code&gt;isAdmissableMessage&lt;/code&gt; checks to make sure that the message the bot is listening to is going into a guild channel, not coming from the bot, starts with the command prefix, and is coming from a member of the guild. Secondly, the message is parsed to get the string connected to prefix, optimistically that is one of the commands being handled, but if it is not then it is ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utils
&lt;/h2&gt;

&lt;p&gt;The only util as of this moment is a parser that ensures consistency oin the command format. I wanted to make a bot that accepted commands similar to a cli and &lt;code&gt;parser.ts&lt;/code&gt; is what holds all this. There are three functions in the file, &lt;code&gt;parseForArguments&lt;/code&gt;, &lt;code&gt;hasAdminPermission&lt;/code&gt;, and &lt;code&gt;argumentsFulfilled&lt;/code&gt;. These functions ensure that the message comes in like cli arguments.&lt;/p&gt;

&lt;p&gt;Here is an example of a proper command: &lt;code&gt;$deposit amount=10 @person&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  parseForArguments
&lt;/h3&gt;

&lt;p&gt;The message is split based on the spaces. The function accepts an array of strings that will function as the keys that are searched for in each of the strings in the newly made array. When in the key is found then the string is split again based on the "=" and returns the second part, optimistically that would be the correct variable. The function also accepts a boolean indicating whether the command requires a mention. The mention is for commands where you need to identify a specific guild member.&lt;/p&gt;

&lt;h3&gt;
  
  
  argumentsFulfilled
&lt;/h3&gt;

&lt;p&gt;This function accepts the same array of strings that are keys for the command. This function makes sure that each one of those keys is returned from the parseForArguments function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cogs
&lt;/h2&gt;

&lt;p&gt;This directory would be where you can add aspects into the bot. For example, I started an Economy cog with the class Economy which has a method handling each of the accepted commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Ces-D/Space-Invader-Bot"&gt;Space-Invader-Bot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How I Built An Incomplete CMS </title>
      <dc:creator>Ces-D</dc:creator>
      <pubDate>Wed, 23 Jun 2021 17:49:28 +0000</pubDate>
      <link>https://dev.to/cesd/how-i-built-an-incomplete-cms-4fbn</link>
      <guid>https://dev.to/cesd/how-i-built-an-incomplete-cms-4fbn</guid>
      <description>&lt;p&gt;I began building a blog application for a site that I may or may not finish. The bulk of the project is based on the &lt;a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter-typescript"&gt;&lt;code&gt;blog-starter-typescript&lt;/code&gt;&lt;/a&gt; example in the NextJs GitHub repo. Their example loads markdown files from a folder in the root directory and uses &lt;a href="https://www.npmjs.com/package/remark"&gt;&lt;code&gt;remark&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/gray-matter"&gt;&lt;code&gt;gray-matter&lt;/code&gt;&lt;/a&gt; to convert the markdown into html and to get file metadata. The example code for these function is located in their &lt;a href="https://github.com/vercel/next.js/blob/canary/examples/blog-starter-typescript/lib"&gt;lib&lt;/a&gt; folder as &lt;strong&gt;api.ts&lt;/strong&gt; and &lt;strong&gt;markdownToHtml.ts&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The system they used works very well, however, if you plan on creating many posts and incorporating many images then that is when the system begins to breakdown. Since you store all of the markdown files and images in your root directory then it will perpetually grow and hosting costs for packages that large are affected. This is where I was looking for a solution that would be light weight and adaptable.&lt;/p&gt;

&lt;p&gt;In my search for a solution I considered databases to store the content. I initially considered a postgres relational database because that is what the typical first choice is when considering databases, but maintaining flexibility in the kind of content that is included in the post (videos, images, etc.) was important to me. Next I considered non-relational databases, but opted for my third option. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/s3/"&gt;AWS S3&lt;/a&gt; buckets can hold a wide variety of objects in storage and can be accessed through Node APIs. This seemed like the best option because I would be able to scale the number of posts I make indefinitely while not affecting my file size and I could maintain the already existing and tested markdownToHtml conversion. It is possible to store markdown files in AWS S3 buckets and then &lt;a href="https://nodejs.org/api/stream.html"&gt;stream&lt;/a&gt; the contents into my existing functions. Before going any further, here is are the dependencies from my &lt;code&gt;package.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"dependencies": {
    "@aws-sdk/client-s3": "^3.18.0",
    "@fontsource/signika-negative": "^4.4.5",
    "@fontsource/source-code-pro": "^4.4.5",
    "gray-matter": "^4.0.3",
    "next": "10.2.3",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "rehype-document": "^5.1.0",
    "rehype-sanitize": "^4.0.0",
    "rehype-stringify": "^8.0.0",
    "remark": "^13.0.0",
    "remark-parse": "^9.0.0",
    "remark-rehype": "^8.1.0",
    "slug": "^5.0.1",
    "unified": "^9.2.1"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created a new file called &lt;code&gt;awsHandler.ts&lt;/code&gt; to store the AWS related functions. In my &lt;code&gt;index.tsx&lt;/code&gt; file, which is typically found in the &lt;code&gt;src/pages/&lt;/code&gt; folder of NextJs application I would list the excerpts and descriptions of the blog posts. Initially this was done by reading all the markdown files contained in the root directory but now was changed to reading all the objects in S3 bucket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function requestAllBucketObjects(): Promise&amp;lt;RequestAllBucketObjectsOutput&amp;gt; {
  const command = new ListObjectsV2Command({ Bucket: BUCKET_NAME, MaxKeys: 10 });
  const objectsOutput = await client.send(command);

  let promises: Promise&amp;lt;GetObjectCommandOutput&amp;gt;[] = [];
  let contentKeys: string[] = [];

  objectsOutput.Contents?.forEach((content) =&amp;gt; {
    if (content.Key) {
      promises.push(requestSingleBucketObject(content.Key));
      contentKeys.push(content.Key);
    } else {
      return; //skip
    }
  });
  let allBucketObjects: GetObjectCommandOutput[] = await Promise.all(promises);
  return { allBucketObjects: allBucketObjects, contentKeys: contentKeys };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that I return an object with allBucketObjects and contentKeys. This is where the incomplete aspect comes in. Using the sdk I am only able to search for specific bucket objects using the key in the form &lt;code&gt;{BUCKET_NAME}/{FILE_PATH}.{FILE_TYPE}&lt;/code&gt;. I use these keys to request the metadata that I previously grabbed using gray-matter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function getPosts(fields: string[] = []) {
  let posts: Items[] = [];
  const { allBucketObjects, contentKeys } = await requestAllBucketObjects();
  let promises: Promise&amp;lt;string&amp;gt;[] = [];

  allBucketObjects.forEach((output) =&amp;gt; {
    promises.push(getMarkdownBody(output));
  });
  const markdowns = await Promise.all(promises);

  markdowns.forEach((markdown, index) =&amp;gt; {
    const key = contentKeys[index];
    posts.push(organizePostItems(markdown, key, fields));
  });

  return posts;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interestingly I was only able to use Node streams when requesting Bucket Objects from S3 requiring me to ensure the stream is &lt;strong&gt;Readable&lt;/strong&gt; then adding each data chunk into a string. I need more practice with streams because ideally I would be able to stream and convert the entire function as it is being read instead of in  the following format which defeats the purpose of the stream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function streamToString(readable: Readable): Promise&amp;lt;string&amp;gt; {
  let data = "";
  for await (const chunk of readable) {
    data += chunk;
  }
  return data;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So why is this a CMS. It is a loose term in this instance because I am using AWS S3 as my storage and a few general functions to control requesting, handling, and presenting the incoming data. &lt;/p&gt;

</description>
      <category>typescript</category>
      <category>aws</category>
      <category>markdown</category>
    </item>
  </channel>
</rss>
