Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Nihlus committed Jul 7, 2024
0 parents commit 6d8ed51
Show file tree
Hide file tree
Showing 20 changed files with 1,529 additions and 0 deletions.
151 changes: 151 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
Contributing
============
Welcome to Remora.Twitch's contributing guidelines! I'm glad you've decided to read this document, and I hope that
it'll be helpful in any contributions you make to the library.

Generally, this document will serve to give you some background on design choices made in the library, as well as
to highlight things to be aware of while developing new features or fixing bugs. If there are any questions or
irregularities that can't be answered by this document, open an issue and ask for assistance - I'll do my best to
clarify and answer.

# Table of Contents
1. [Goals](#1-goals)
1. [Correctness](#11-correctness)
2. [Robustness](#12-robustness)
3. [True Asynchronicity and Concurrency](#13-true-asynchronicity-and-concurrency)
2. [Structure](#2-structure)
3. [How to Contribute](#3-how-to-contribute)
4. [Tips & Tricks](#4-tips--tricks)

## 1. Goals
To understand many of the design choices, you must first understand the three pillar goals of Remora.Twitch, and why
they are the way they are. Remora.Twitch originates from the original author's frustration with many inconsistencies
in various APIs in the C#/Twitch ecosystem, both in relation to the Twitch API itself and the language usage within
existing solutions. The goals below were set early in the development process to guide development away from these
problems, and to find parts of the user experience that should be placed at the forefront of the library.

Therefore, Remora.Twitch defines the following three goals.

### 1.1 Correctness
Correctness, in the context of Remora.Twitch, means that the API available to the end user should as faithfully and
accurately represent the actual reality of data presented to or from an API; that is, no data or structure of data
should meaningfully change between the library receiving it and the user accessing it.

### 1.2 Robustness
Robustness refers to a focus on never allowing problems originating from user data or real-life runtime conditions to
bring down or otherwise corrupt the end user's application. The end user should be confident that, should an error
arise, they will be aware of the fault potential before even compiling the application.

Any method that has a fault potential should be declared in such a way that the user must consider whether the operation
was successful before proceeding. To this end, Remora.Twitch further breaks issues down into the following categories:

* Programmer errors
* User errors
* Environment errors

Programmer errors are caused by incorrect, invalid, or inappropriate use of Remora.Twitch's API that cannot be caught
at compile time. These errors should, as early as possible, throw an exception to prevent the invalid usage from making
it out of the development phase.

```c#
_object.CallMeAfterA(); // throws InvalidOperationException
_object.A();
```

User errors originate from externally sourced input to the library, such as payloads from Twitch, or data from the end
user provided in a method call. These errors typically involve Remora.Twitch encountering data it is unable to parse,
data it is unfamiliar with, or data violating some form of constraint. The primary distinction from programmer errors
is that these issues may appear frequently or nondeterministically, usually due to the end user varying their input.

```c#
var result = _object.PerformPotentiallyFailingAction();
if (result.IsSuccess)
{
...
}
```

In all cases, this category of errors should result in an unsuccessful return type being returned by the callee.

Environment errors stem from indirect problems; network outages, disk space, memory runout, etc. While in their own
category due to their unpredictability, they are treated and reported the same way as user errors.

### 1.3 True Asynchronicity and Concurrency
Remora.Twitch aims to be truly asynchronous from the ground up, respecting and utilizing established best practices for
C# and the TPL. Furthermore, it aims to be concurrent, allowing end users to react to and perform actions upon many
incoming events at once.

Everything that Remora.Twitch provides to the end user which involves some form of either IO- or CPU-bound work is
presented as a task, and it assumes everything a user wishes to perform in registered callbacks or customized services
may become IO or CPU bound.

Any asynchronous operation is also designed to be cancellable, allowing clean terminations of processes and units of
work.

## 2. Structure
The library is structured into three main parts - the abstractions, the concrete reference implementations, and a
high-level layer.

At its core, the library exposes nothing but abstract interfaces without any implementations behind
them, serving only as a common and basic 1:1 mapping to the Twitch API (with some allowances made for C#-ification of
data types). This abstraction layer has no external dependencies and no business logic whatsoever.

On top of that, a concrete reference implementation is defined, where the abstraction layer is used to build a base
library that actually does the work against the real API - serializing JSON, mapping properties, setting up websockets,
communicating over HTTP, etc; this library serves as the default implementation for any consumers that make use of the
abstractions in their projects.

Finally, a high-level layer utilizes the default implementation to provide a more consumer-friendly and C#-oriented API
for interacting with Twitch - caching, setting nicknames, sending messages, joining servers, etc. This high-level layer
also provides an abstraction layer; this is what consumers should primarily use.

This structure enables Remora.Twitch to first and foremost define a de facto C# mapping of the Twitch API that any
library can implement, and end user applications will not have to adapt or be rewritten to support specific libraries.
Beyond that usage potential, Remora.Twitch also takes the role of an implementer and provides a usable backend for the
abstractions.

## 3. How to Contribute
To contribute to the library, start by browsing open issues and seeing if anything catches your eye & interest. If it
does, fork the project (if you haven't already) and create a separate branch for your changes. Open a pull request early
and claim the issue, then begin working. Following the goal guidelines and input from other community members, finalize
your changes into a state where you're happy with them, then request a review and merge from a maintainer.

Once your pull request clears the review phase, it'll be merged. If any changes are necessary, you will be alerted
during the review phase and a maintainer will work with you to ensure your changes are in line with the library's goals
and up to snuff.

Generally, code at the review phase is expected to fulfill the following requirements; that is, that it:

* Compiles. Code that does not compile will instantly fail code review.
* Follows the library's syntax standards. In most cases, the compiler will yell at you if you do something
incorrectly. If not, a maintainer will most likely catch it during review and send it back so you can fix it.
* Has unit tests. If your contribution adds code that is uncovered by tests, you are expected to also write tests for
it that has as high a coverage as is realistically possible.
* Passes all tests. If you have failing unit tests, you must correct either your changes or the tests to pass and
appropriately test the changes.
* Follows the library goals. See the goals above; this is a "soft" requirement, in that there are no hard rules - it's
the spirit of the goals that matters. If a maintainer believes that something is implemented contrary to the goals,
they will alert you during the review phase, or earlier if they've noticed by themselves.

## 4. Tips & Tricks
In order to more quickly write code that's in line with the library standards, consider applying the following tips &
tricks while developing.

* Fail fast, fail often. If your code takes an input, verify it as early as possible and return an error (or throw, if
appropriate) as quickly as you can. If you call a method that has failure potential, always check the results.
* Minimize indentation. Keep things as flat and linear as you can, avoiding deep nesting. Invert conditionals where
possible; prefer `if (!condition)` and returning over `if (condition)` and performing actions inside the scope.
* Avoid throwing exceptions. Exceptions should only be used for programmer errors, as defined in [Goals](#1-goals).
* Avoid catching exceptions in inner scopes. If something does go wrong, it should bubble up as far as possible before
being caught and wrapped in a result.
* Never let exceptions bubble up into user code. While exceptions should bubble up as far as possible, it should
*never* reach user code. An uncaught exception that bubbles up into user code is considered a library bug.
* Formulate boolean parameters and properties as questions. Write `ShouldDoThing` instead of `DoThing`, `IsDone`
instead of `Done`, etc.
* Avoid returning `null`. `null` is to be considered an exceptional value, and should not be returned without a very
good reason. Similarly, `null` should not be accepted as a valid input unless explicitly intended.
* Use C#8's nullability annotations. Be clear and concise with your intent.
* Don't surprise the caller. Follow the principle of least astonishment - if a user would be surprised by what your
code does, it's probably time to change it.
* Name methods and variables descriptively. It's better to have a long and descriptive name than a short and
abbreviated one.
43 changes: 43 additions & 0 deletions .github/ISSUE_TEMPLATE/bug-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Based on an issue template from the Discord API documentation.
name: Bug Report
description: A bug has been found in the library
labels: ["bug"]
body:
- type: markdown
attributes:
value: "Before opening a new issue, please search existing issues: https://github.com/Remora/Remora.Twitch/issues?q=is%3Aissue+label%3Abug"
- type: textarea
id: description
attributes:
label: Description
description: Provide a clear and concise description of what the problem is.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: Provide specific, clear, and concise steps for us to reliably reproduce this issue.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: What is the behavior you expect to occur that is not?
validations:
required: true
- type: textarea
id: current
attributes:
label: Current Behavior
description: What is the behavior you are currently seeing instead?
validations:
required: true
- type: textarea
id: information
attributes:
label: Library / Runtime Information
description: Which version of the library is this happening on? Which version of the C# runtime?
validations:
required: true
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Developer Documentation
url: https://remora.github.io/Remora.Twitch/main/articles/intro.html
about: Need documentation and examples for the API? Head over to Remora.Twitch's documentation.
- name: Discord Server
url: https://discord.gg/dyYmwashVs
about: Need help with the library? Talk to us in our Discord server.
36 changes: 36 additions & 0 deletions .github/ISSUE_TEMPLATE/feature-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Based on an issue template from the Discord API documentation.
name: Feature Request
description: Suggestions for new or different behavior in the library
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: "Before opening a new issue, please search existing issues: https://github.com/Remora/Remora.Twitch/issues?q=is%3Aissue+label%3Aenhancement"
- type: textarea
id: description
attributes:
label: Description
description: Provide a clear and concise description of what the feature request is.
validations:
required: true
- type: textarea
id: needed
attributes:
label: Why This is Needed
description: Provide a clear and concise explanation of what problem this solves for you.
validations:
required: true
- type: textarea
id: alternative
attributes:
label: Alternatives Considered
description: There's usually more than one way to solve a problem. What are some other alternatives you've considered, if any?
validations:
required: true
- type: textarea
id: details
attributes:
label: Additional Details
description: Is there anything else you can add about this feature request?
validations:
required: false
2 changes: 2 additions & 0 deletions .github/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ShowInNavbar: false
-------------------
107 changes: 107 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: .NET

env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
GITHUB_USER: Nihlus
GITHUB_FEED: https://nuget.pkg.github.com/Remora
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

permissions:
pull-requests: write
contents: write
packages: write
security-events: write

on:
push:
branches: [ main ]
tags: [ '*' ]
pull_request:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
- name: Build
run: |
dotnet restore
dotnet build -c Release --no-restore
- name: Test
run: dotnet test -c Release --no-restore --no-build --verbosity minimal --collect:"XPlat Code Coverage" --results-directory ./coverage

- name: Inspect
uses: JetBrains/[email protected]
with:
tool-version: 2023.3.4
solution: Remora.Twitch.sln
build: false
no-build: true
telemetry-optout: true

- name: Coverage
uses: irongut/[email protected]
with:
filename: coverage/**/coverage.cobertura.xml
badge: true
format: markdown
indicators: true
output: both
thresholds: '60 80'

- uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request'
with:
name: coverage
path: code-coverage-results.md

- name: Package
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: dotnet pack -c Release --no-restore --no-build --version-suffix "github$GITHUB_RUN_ID"

- uses: actions/upload-artifact@v4
with:
name: nupkg
path: nuget/*

publish_prerelease_packages:
name: Publish Prerelease Packages
needs: build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest

steps:
- name: Download package artifacts
uses: actions/download-artifact@v4
with:
name: nupkg
path: nuget

# To ensure that the current version being pushed does not get pruned we prune first.
- name: Prune packages older than 4 versions (new version is the 5th)
uses: smartsquaregmbh/[email protected]
with:
organization: Remora
type: nuget
keep: 4
names: |
Remora.Twitch
- name: Push to GitHub Feed
run: |
for f in ./nuget/*; do
curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED
done
30 changes: 30 additions & 0 deletions .github/workflows/pr-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Comment on pull request
on:
workflow_run:
workflows:
- .NET
types:
- completed

jobs:
comment:
runs-on: ubuntu-latest
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
steps:
- name: Download coverage results
uses: actions/download-artifact@v4
with:
name: coverage
path: code-coverage-results.md
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Add coverage PR comment
uses: marocchino/sticky-pull-request-comment@v2
with:
recreate: true
path: code-coverage-results.md

Loading

0 comments on commit 6d8ed51

Please sign in to comment.