{"id":23643612,"url":"https://github.com/mcpringle/apus","last_synced_at":"2025-08-31T20:31:03.415Z","repository":{"id":229661370,"uuid":"776777258","full_name":"McPringle/apus","owner":"McPringle","description":"Apus is a social media wall for conferences.","archived":false,"fork":false,"pushed_at":"2025-08-19T23:03:30.000Z","size":6443,"stargazers_count":13,"open_issues_count":28,"forks_count":18,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-20T01:07:01.409Z","etag":null,"topics":["bluesky","conference","conferences","flow","java","mastodon","social-media","social-network","vaadin","vaadin-flow"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/McPringle.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-03-24T13:16:18.000Z","updated_at":"2025-08-19T23:03:33.000Z","dependencies_parsed_at":"2025-04-28T03:21:45.075Z","dependency_job_id":"af859674-04f8-4755-99c5-5584599c03d5","html_url":"https://github.com/McPringle/apus","commit_stats":null,"previous_names":["mcpringle/apus"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/McPringle/apus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/McPringle%2Fapus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/McPringle%2Fapus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/McPringle%2Fapus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/McPringle%2Fapus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/McPringle","download_url":"https://codeload.github.com/McPringle/apus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/McPringle%2Fapus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273034568,"owners_count":25034433,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-31T02:00:09.071Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bluesky","conference","conferences","flow","java","mastodon","social-media","social-network","vaadin","vaadin-flow"],"created_at":"2024-12-28T11:32:21.946Z","updated_at":"2025-08-31T20:31:03.408Z","avatar_url":"https://github.com/McPringle.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Apus\n\n[![All Tests](https://github.com/McPringle/apus/actions/workflows/all-tests.yml/badge.svg)](https://github.com/McPringle/apus/actions/workflows/all-tests.yml)\n[![codecov](https://codecov.io/gh/McPringle/apus/graph/badge.svg?token=OPDR66ID7D)](https://codecov.io/gh/McPringle/apus)\n\n## Contents\n\n* [About](#about)\n* [Mission Statement](#mission-statement)\n* [Screenshots](#screenshots)\n* [Features](#features)\n* [Running in Production](#running-in-production)\n  * [Using Docker](#using-docker)\n  * [Using Podman](#using-podman)\n* [Configuration](#configuration)\n  * [Configuration Options](#configuration-options)\n  * [Custom Styles](#custom-styles)\n  * [Create Hashed Password](#create-hashed-password)\n  * [Configuration Files](#configuration-files)\n* [Contributing](#contributing)\n  * [Good First Issues](#good-first-issues)\n  * [Sign-off your commits](#sign-off-your-commits)\n  * [Add an emoji to your commit](#add-an-emoji-to-your-commit)\n  * [AI Generated Code](#ai-generated-code)\n  * [Build](#build)\n  * [Debugging](#debugging)\n  * [Packaging](#packaging)\n  * [Writing Plugins](#writing-plugins)\n* [Communication](#communication)\n* [Contributors](#contributors)\n* [Copyright and License](#copyright-and-license)\n\n## About\n\n***Apus* is a social media wall for conferences.**\n\nThe name *Apus* is based on Apus Apus, Latin for common swift. This is a bird species that is extremely adapted to a life in the air and can stay in the air for around ten months almost without interruption and can reach speeds of more than 200 km/h during flight maneuvers. This bird breeds in a wall, flies reliably for extremely long periods without crashing and is also extremely fast. Hopefully all of this also applies to Apus: fast execution, uninterrupted and reliable operation!\n\nSee:\n\n* https://en.wikipedia.org/wiki/Common_swift\n* https://en.wikipedia.org/wiki/Apus_(bird)\n\n## Mission Statement\n\n🇬🇧  \n*Apus* connects conference attendees in real time: Our application displays the up-to-date program with current and upcoming presentations as well as live-shared impressions from social networks. We provide orientation, foster interaction, and turn every event into a connected experience.\n\n🇩🇪  \n*Apus* bringt Konferenzteilnehmer in Echtzeit zusammen: Unsere Anwendung zeigt das aktuelle Programm mit laufenden und bevorstehenden Vorträge sowie live geteilte Eindrücke aus sozialen Netzwerken. So schaffen wir Orientierung, fördern Interaktion und machen jede Veranstaltung zu einem vernetzten Erlebnis.\n\n## Screenshots\n\n*Apus* supports localization. Screenshots are in different languages.\n\n![Apus screenshot with default theme](screenshot-default.webp)\n*Apus screenshot with default theme*\n\n![Apus screenshot with BaselOne theme](screenshot-baselone.webp)\n*Apus screenshot with BaselOne theme*\n\n![Apus screenshot with Java Forum Stuttgart theme](screenshot-jfs.webp)\n*Apus screenshot with Java Forum Stuttgart theme*\n\n![Apus screenshot with JavaLand theme](screenshot-javaland.webp)\n*Apus screenshot with JavaLand theme*\n\n![Apus screenshot with Voxxed Days theme](screenshot-voxxeddays.webp)\n*Apus screenshot with Voxxed Days theme*\n\n## Features\n\n### Show posts from Social Media\n\n| Platform    | Status    |\n|-------------|-----------|\n| BlueSky     | SUPPORTED |\n| Mastodon    | SUPPORTED |\n\n### Show event agenda\n\n| Event                | Plugin                     |\n|----------------------|----------------------------|\n| BaselOne             | `SessionizePlugin`         |\n| CloudLand            | `DoagPlugin`               |\n| CyberLand            | `DoagPlugin`               |\n| Devoxx               | `DevoxxPlugin`             |\n| Java Forum Nord      | `SessionizePlugin`         |\n| Java Forum Stuttgart | `JavaForumStuttgartPlugin` |\n| JavaLand             | `DoagPlugin`               |\n| KI Navigator         | `DoagPlugin`               |\n| Voxxed Days          | `DevoxxPlugin`             |\n\n### And more\n\n| Feature                   | Status    |\n|---------------------------|-----------|\n| Running text with updates | - TODO -  |\n| Show sponsor information  | SUPPORTED |\n| Use event based styling   | SUPPORTED |\n\n## Running in Production\n\nIt is highly recommended to use [Docker](https://www.docker.com/) or [Podman](https://podman.io/) to run *Apus* in production. Here follows a very short explanation of the example commands below. Consult the Docker or Podman documentation for more information about all available options for running an image.\n\n| Option         | Explanation                                   |\n|----------------|-----------------------------------------------|\n| --name apus    | Specify the name for the running instance.    |\n| -p 80:8080     | Make *Apus* available on host port 80         |\n| -e KEY=value   | Configure *Apus* using environment variables. |\n| -d             | Run *Apus* in daemon mode (background).       |\n| --rm           | Remove the container when stopping *Apus*.    |\n| mcpringle/apus | The Docker image to be started.               |\n\nModify the following commands according to your needs and consult the [configuration section](#configuration) below for more information about how to configure *Apus*. The Docker image of *Apus* will be pulled from [Docker Hub](https://hub.docker.com/) automatically when not available locally.\n\n### Using Docker\n\n```shell\ndocker run \\\n    --name apus \\\n    -p 80:8080 \\\n    -e APUS_SOCIAL_HASHTAGS=java \\\n    -e APUS_TIMEZONE=Europe/Zurich \\\n    -d \\\n    --rm \\\n    mcpringle/apus\n```\n\n### Using Podman\n\nThe parameters are the same like in in the Docker example, but the image must be prefixed with `docker.io/`:\n\n```shell\npodman run \\\n    --name apus \\\n    -p 80:8080 \\\n    -e APUS_SOCIAL_HASHTAGS=java \\\n    -e APUS_TIMEZONE=Europe/Zurich \\\n    -d \\\n    --rm \\\n    docker.io/mcpringle/apus\n```\n\n## Configuration\n\n*Apus* can be started without any specific configuration. All configuration options have working default values.\n\n### Configuration Options\n\nTo modify the default configuration values, just specify environment variables with the following names:\n\n| Environment Variable            | Default       | Description                                                                           |\n|---------------------------------|---------------|---------------------------------------------------------------------------------------|\n| APUS_BLUESKY_HASHTAG_URL        | [1]           | The URL of the BlueSky API to get the posts containing a hashtag (empty = disabled).  |\n| APUS_BLUESKY_INSTANCE           | api.bsky.app  | The BlueSky instance used to read the posts from (empty = disabled).                  |\n| APUS_BLUESKY_MENTIONS_URL       | [2]           | The URL of the BlueSky API to get the posts mentioning a profile (empty = disabled).  |\n| APUS_BLUESKY_POST_LIMIT         | 30            | The limit for the number of BlueSky posts when accessing the API.                     |\n| APUS_BLUESKY_PROFILE            |               | The profile (without the @) of a BlueSky user to get the mentions (empty = disabled). |\n| APUS_DEMO                       | false         | Enable (true) or disable (false) the demo mode to show demo data only.                |\n| APUS_DEVOXX_EVENT_API           | [3]           | The URL of the Devoxx API to read the conference agenda.                              |\n| APUS_DEVOXX_EVENT_ID            |               | The ID of the Devoxx event to read the conference agenda.                             |\n| APUS_DEVOXX_WEEKDAY             |               | The day of the week of the Devoxx event to read the conference agenda.                |\n| APUS_DOAG_EVENT_API             | [4]           | The URL of the DOAG event API to read the conference agenda.                          |\n| APUS_DOAG_EVENT_ID              | 0             | The ID of the DOAG event to read the conference agenda (0 = disabled).                |\n| APUS_EVENT_DATE_ADJUST          | P0D           | Adjust the date of the event, ISO-8601 formatted (P0D = disabled).                    |\n| APUS_EVENT_IMAGE_URL            |               | The URL of the image to be shown below the event agenda (empty = no image).           |\n| APUS_EVENT_NEXT_SESSION_TIMEOUT | 60            | Number of minutes a session is shown before it starts (0 = disabled).                 |\n| APUS_EVENT_SHOW_EMPTY_ROOMS     | true          | Show (true) or hide (false) empty event rooms.                                        |\n| APUS_EVENT_SHOW_LEGEND          | true          | Show (true) or hide (false) the event room legend.                                    |\n| APUS_EVENT_TIME_ADJUST          | PT0D          | Adjust the time of the event, ISO-8601 formatted (PT0D = disabled).                   |\n| APUS_EVENT_UPDATE_FREQUENCY     | 5             | How often (in minutes) to update event data (0 = disabled).                           |\n| APUS_JFS_JSON_URL               |               | The URL of the JSON file for Java Forum Stuttgart.                                    |\n| APUS_LANGUAGE                   | en            | Language code of the language used for the UI.                                        |\n| APUS_MASTODON_ACCESS_TOKEN      |               | The Mastodon access token. Only needed for the notification API (see below).          |\n| APUS_MASTODON_INSTANCE          | ijug.social   | The Mastodon instance used to read the posts from (empty = disabled).                 |\n| APUS_MASTODON_LIMIT             | 30            | The limit for the number of results when accessing the Mastodon API.                  |\n| APUS_MASTODON_NOTIFICATION_API  | [5]           | The URL of the Mastodon API to read the notifications (empty = disabled).             |\n| APUS_MASTODON_POST_API          | [6]           | The URL of the Mastodon API to read the posts (empty = disabled).                     |\n| APUS_PASSWORD                   |               | The hashed password to get admin access (empty = disabled).                           |\n| APUS_SOCIAL_FILTER_LENGTH       | 500           | Hide social media posts which exceed this length (0 = disabled).                      |\n| APUS_SOCIAL_FILTER_REPLIES      | true          | Hide social media posts which are replies.                                            |\n| APUS_SOCIAL_FILTER_SENSITIVE    | true          | Hide social media posts which contain sensitive information.                          |\n| APUS_SOCIAL_FILTER_WORDS        |               | Hide social media posts which contain these words.                                    |\n| APUS_SOCIAL_COLUMNS             | 3             | How many columns to be used for social media posts.                                   |\n| APUS_SOCIAL_HASHTAGS            |               | A list of comma separated hashtags for social media posts (empty = disabled).         |\n| APUS_SOCIAL_HEADLINE            |               | Overwrite the headline for social media posts (empty = don't overwrite).              |\n| APUS_SOCIAL_IMAGE_LIMIT         | 1             | Limit number of images per social media post (0 = no limit).                          |\n| APUS_SOCIAL_IMAGES_ENABLED      | true          | Enable or disable images in social media posts.                                       |\n| APUS_SESSIONIZE_EVENT_API       | [7]           | The URL of the Sessionize API to read the conference agenda.                          |\n| APUS_SESSIONIZE_EVENT_ID        | 0             | The ID of the Sessionize event to read the conference agenda (0 = disabled).          |\n| APUS_SESSIONIZE_SPEAKER_API     | [8]           | The URL of the Sessionize API to read the speaker information.                        |\n| APUS_STYLES                     |               | Inject custom styles into the user interface (see explanations below).                |\n| APUS_TIMEZONE                   | Europe/Zurich | The timezone used for date and time calculations.                                     |\n\nThe environment variables will override the default values. Some default values might be too long to be displayed in this table. They are marked with a number in square brackets and can be looked up in the following list:\n\n1. `https://${instance}/xrpc/app.bsky.feed.searchPosts?q=%23${hashtag}\u0026tag=${hashtag}\u0026limit=${limit}`\n2. `https://${instance}/xrpc/app.bsky.feed.searchPosts?q=%40${profile}\u0026mentions=${profile}\u0026limit=${limit}`\n3. `https://${event}.cfp.dev/api/public/schedules/${weekday}`\n4. `https://meine.doag.org/api/event/action.getCPEventAgenda/eventId.${event}`\n5. `https://${instance\\/api/v1/notifications?types[]=mention\u0026limit=${limit}`\n6. `https://${instance}/api/v1/timelines/tag/${hashtag}?limit=${limit}`\n7. `https://sessionize.com/api/v2/${event}/view/Sessions`\n8. `https://sessionize.com/api/v2/${event}/view/Speakers`\n\n#### Adjusting Event Dates and Times\n\nThe `APUS_EVENT_DATE_ADJUST` option uses the ISO-8601 period formats `PnYnMnD` and `PnW`. Examples:\n\n| Example     | Description                                                  |\n|-------------|--------------------------------------------------------------|\n| `P5D`       | +5 days                                                      |\n| `P3M`       | +3 months                                                    |\n| `P2Y`       | +2 years                                                     |\n| `P4W`       | +4 weeks                                                     |\n| `P1Y2M3D`   | +1 year, +2 months, +3 days                                  |\n| `P1Y2M3W4D` | +1 year, +2 months, +25 days (3 weeks and 4 days)            |\n| `P-1Y2M`    | -1 year, +2 months (minus is valid for the year only)        |\n| `-P1Y2M`    | -1 year, -2 months (minus is valid for the whole expression) |\n\nThe `APUS_EVENT_TIME_ADJUST` option uses the ISO-8601 duration formats `PTnHnMnS`. Examples:\n\n| Example    | Description                                                   |\n|------------|---------------------------------------------------------------|\n| `PT5H`     | +5 hours                                                      |\n| `PT3M`     | +3 minutes                                                    |\n| `PT2S`     | +2 seconds                                                    |\n| `PT1H2M3S` | +1 hour, +2 minutes, +3 seconds                               |\n| `PT-1H2M`  | -1 hour, +2 minutes (minus is valid for the hours only)       |\n| `-PT1H2M`  | -1 hour, -2 minutes (minus is valid for the whole expression) |\n\n#### Create Mastodon Access Token\n\n1. Please log in at https://YOUR.INSTANCE/.\n2. Open the page https://YOUR.INSTANCE/settings/applications/new.\n3. Fill out the form:\n   - Application name: Apus\n   - Application website: https://APUS_DOMAIN/\n   - Adjust the permissions as follows:\n     - Enable only \"read:notifications\"\n     - Disable everything else\n4. Scroll to the bottom and click \"Save\".\n5. The new application \"Apus\" will now appear in the table.\n6. Click on the name \"Apus\".\n7. Copy the access token (third line).\n\nReplace the placeholder `YOUR.INSTANCE` with the domain of the Mastodon instance where your account is registered (e.g. `mastodon.social` or `ijug.social`) and replace `APUS_DOMAIN` with the domain where your Apus installation is accessible (e.g. `apus.ijug.eu`).\n\n### Custom Styles\n\nYou can modify the styles of the user interface using CSS variables. The CSS variables must be set using the environment variable `APUS_STYLES` in key and value pairs. The key is the variable to be set and must start with two dashes (`--`). Keys and values must be separated by a colon (`:`). Multiple key and value pairs are separated by a semicolon (`;`). Example:\n\n```\n--name-of-variable-one:value1;--name-of-variable-two:value2\n```\n\nThe following table contains the CSS variables you can modify to change the user interface and their default values:\n\n| CSS Variable                      | Default           | Description                                                        |\n|-----------------------------------|-------------------|--------------------------------------------------------------------|\n| --event-background-color          | #e7eaee           | The color for the background of the event agenda.                  |\n| --event-title-color               | #262626           | The color for the title of the event agenda.                       |\n| --event-text-color                | #262626           | The color for the text of the event agenda.                        |\n| --event-running-session-color     | #ffffff           | The color for the background of rooms with running sessions.       |\n| --event-next-session-color        | #eeeeee           | The color for the background of rooms with sessions starting next. |\n| --event-closed-room-color         | #cccccc           | The color for the background of closed rooms.                      |\n| --event-room-border               | 1px solid #909090 | The border for the event room.                                     |\n| --event-image-position-bottom     | 10px              | The position of the optional event image relative to the bottom.   |\n| --event-image-position-left       | 10px              | The position of the optional event image relative to the left.     |\n| --event-image-width               | auto              | The width of the optional event image.                             |\n| --event-image-height              | auto              | The height of the optional event image.                            |\n| --social-background-color         | #e7eaee           | The color for the background of the social wall.                   |\n| --social-title-color              | #262626           | The color for the title of the social wall.                        |\n| --social-text-color               | #262626           | The color for the text of the social wall.                         |\n| --social-post-background-color    | #ffffff           | The color for the background of social posts.                      |\n| --social-post-border              | 1px solid #909090 | The border for the social posts.                                   |\n| --speaker-avatar-background-color | transparent       | The color for the background of speaker avatars.                   |\n| --speaker-avatar-border           | none              | The border for the speaker avatars.                                |\n\n\u003e [!NOTE]  \n\u003e Default values may change in newer versions of *Apus*. Compare your custom styles before and after an update.\n\n#### Example Styles\n\n##### BaselOne\n\n```\n--event-background-color: #9a1445;\n--event-title-color: #ffffff;\n--event-text-color: #ffffff;\n--event-running-session-color: #7248f0;\n--event-next-session-color: #7248f0;\n--event-closed-room-color: #7248f0;\n--event-room-border: 1px solid #000000;\n--event-image-border: 1px solid #000000;\n--event-image-width: 250px;\n--event-image-height: 250px;\n\n--social-background-color: #7248f0;\n--social-title-color: #ffffff;\n--social-text-color: #000000;\n--social-post-background-color: #ffffff;\n--social-post-border: 1px solid #000000;\n\n--speaker-avatar-background-color: transparent;\n--speaker-avatar-border: none;\n```\n\n##### Java Forum Stuttgart\n\n```\n--event-background-color: #fcb913;\n--event-title-color: #000000;\n--event-text-color: #000000;\n--event-running-session-color: #fff8dd;\n--event-next-session-color: #fff8dd;\n--event-closed-room-color: #fff8dd;\n--event-room-border: 1px solid #000000;\n--event-image-border: 1px solid #000000;\n--event-image-width: 852px;\n\n--social-background-color: #fff8dd;\n--social-title-color: #000000;\n--social-text-color: #000000;\n--social-post-background-color: #ffffff;\n--social-post-border: 1px solid #fcb913;\n```\n\n##### JavaLand\n\n```\n--event-background-color: #000000;\n--event-title-color: #ffffff;\n--event-text-color: #262626;\n--event-running-session-color: #79fffe;\n--event-next-session-color: #00f1ef;\n--event-closed-room-color: #00c2c1;\n--event-room-border: none;\n\n--social-background-color: #272727;\n--social-title-color: #ffffff;\n--social-text-color: #262626;\n--social-post-background-color: #ffa8a8;\n--social-post-border: none;\n```\n\n##### Voxxed Days Zürich\n\n```\n--event-background-color: #4c4f53;\n--event-title-color: #8ed1fc;\n--event-text-color: #000000;\n--event-running-session-color: #ebfffc;\n--event-next-session-color: #ebfffc;\n--event-closed-room-color: #ebfffc;\n--event-room-border: none;\n--event-image-position-bottom: 10px;\n--event-image-position-left: 10px;\n--event-image-width: 425px;\n\n--social-background-color: #58c3f0;\n--social-title-color: #ffffff;\n--social-text-color: #000000;\n--social-post-background-color: #ebf7ff;\n--social-post-border: none;\n\n--speaker-avatar-background-color: transparent;\n--speaker-avatar-border: none;\n```\n\n### Create Hashed Password\n\nFor security reasons the password is not stored in cleartext. *Apus* requires the password to be hashed using [bcrypt](https://en.wikipedia.org/wiki/Bcrypt). Of course, *Apus* can do this for you. Start the JAR file providing the parameter `-p` followed by the password you want to create a hash for. The output will show you two lines of code. The first line contains the hashed password and the second line contains the same hashed password, but with the dollar signs escaped ready to copy and paste it into a `docker-compose.yaml`. Examples:\n\n#### Using the Command Line\n\n```\njava -jar apus.jar -p 12345\nHashed password for environment variable: $2a$10$nybQbl/iY8SRJkfHJVncS.L5.OC3KJ6VRBYVAID7qnUqwylmn/BtK\nHashed password for Docker Compose file: $$2a$$10$$nybQbl/iY8SRJkfHJVncS.L5.OC3KJ6VRBYVAID7qnUqwylmn/BtK\n```\n\n#### Using Docker\n\n```\ndocker run mcpringle/apus java -jar /usr/app/app.jar -p 12345\nHashed password for environment variable: $2a$10$nybQbl/iY8SRJkfHJVncS.L5.OC3KJ6VRBYVAID7qnUqwylmn/BtK\nHashed password for Docker Compose file: $$2a$$10$$nybQbl/iY8SRJkfHJVncS.L5.OC3KJ6VRBYVAID7qnUqwylmn/BtK\n```\n\n#### Using Podman\n\n```\npodman run docker.io/mcpringle/apus java -jar /usr/app/app.jar -p 12345\nHashed password for environment variable: $2a$10$nybQbl/iY8SRJkfHJVncS.L5.OC3KJ6VRBYVAID7qnUqwylmn/BtK\nHashed password for Docker Compose file: $$2a$$10$$nybQbl/iY8SRJkfHJVncS.L5.OC3KJ6VRBYVAID7qnUqwylmn/BtK\n```\n\n### Configuration Files\n\nAll configuration files are completely optional and stored in an `.apus` subdirectory of the home directory of the user running *Apus*.\n\n| File              | Description                                           |\n|-------------------|-------------------------------------------------------|\n| `blockedProfiles` | This file contains blocked profiles, one per line.    |\n| `hiddenPostIds`   | This file contains IDs of hidden posts, one per line. |\n\nIf you are running *Apus* in a Docker or Podman container and want to keep your configuration files between restarts, you can bind the `.apus` directory of the running container to an existing directory on your host system. Important: The directory on your host system must exist and must be writeable by the *Apus* user inside the container (which is user ID 1000 and group ID 1000). Consult the documentation of your host operating system if you need information about how to set the correct access rights.\n\nExamples for binding the `.apus` directory of the running container to an existing directory on your host system using the `-v` option of Docker or Podman:\n\n#### Using Docker\n\n```shell\ndocker run \\\n    --name apus \\\n    -p 80:8080 \\\n    -v $HOME/mydir:/home/apus/.apus \\\n    -e APUS_SOCIAL_HASHTAGS=java \\\n    -e APUS_TIMEZONE=Europe/Zurich \\\n    -d \\\n    --rm \\\n    mcpringle/apus\n```\n\n#### Using Podman\n\n```shell\npodman run \\\n    --name apus \\\n    -p 80:8080 \\\n    -v $HOME/mydir:/home/apus/.apus \\\n    -e APUS_SOCIAL_HASHTAGS=java \\\n    -e APUS_TIMEZONE=Europe/Zurich \\\n    -d \\\n    --rm \\\n    docker.io/mcpringle/apus\n```\n\n## Contributing\n\n### Good First Issues\n\nTo find possible tasks for your first contribution to *Apus*, we tagged some of the hopefully easier to solve issues as [good first issue](https://github.com/McPringle/apus/labels/good%20first%20issue).\n\nIf you prefer to meet people in real life to contribute to *Apus* together, we recommend to visit a [Hackergarten](https://www.hackergarten.net/) event. *Apus* is often selected as a contribution target in [Lucerne](https://www.meetup.com/hackergarten-luzern/), [Zurich](https://www.meetup.com/hackergarten-zurich/), and at the [JavaLand](https://www.javaland.eu/) conference.\n\nPlease join our developer community using our [Matrix chat](#matrix-chat) to get support and help for contributing to *Apus*.\n\n### Sign-off your commits\n\nIt is important to sign-off *every* commit. That is a de facto standard way to ensure that *you* have the right to submit your content and that you agree to the [DCO](DCO.md) (Developer Certificate of Origin).\n\nYou can find more information about why this is important and how to do it easily in a very good [blog post](https://dev.to/janderssonse/git-signoff-and-signing-like-a-champ-41f3)  by Josef Andersson.\n\n### Add an emoji to your commit\n\nWe love to add an emoji to the beginning of every commit message which relates to the nature of the change. You can find a searchable list of possible emojis and their meaning in the overview on the [gitmoji](https://gitmoji.dev/) website. If you prefer, you can also install one of the plugins that are available for almost all common IDEs.\n\n### JSpecify, NullAway, and Error Prone\n\nIn this project we use [JSpecify](https://jspecify.dev/) together with [NullAway](https://github.com/uber/NullAway) and [Error Prone](https://errorprone.info/) to eliminate possible `NullPointerException`s. To make this work, each package must contain a `package-info.java` file with the following content:\n\n```java\n/**\n * LICENSE HEADER\n */\n@NullMarked\npackage swiss.fihlon.apus.PACKAGE;\n\nimport org.jspecify.annotations.NullMarked;\n```\n\nPlease use the same license header as in each other Java file. The annotation `@NullMarked` comes from [JSpecify](https://jspecify.dev/) and ensures that all reference types within a package are treated as `@NonNull` by default, unless they are explicitly annotated with `@Nullable`.\n\nPlease make extensive use of the `@Nullable` and `@NotNull` annotations in the while *Apus* code base. Annotate every method, method parameter, and return type with the appropriate annotation.As a result, every major IDE will warn you if you access something that can be `null` without checking it first. As additional protection, the [Error Prone Plugin](https://errorprone.info/) ensures that the build fails with a corresponding error.\n\nThis way, no more `NullPointerException`s should be thrown at runtime.\n\n### AI Generated Code\n\nAI generated source code is based on real existing source code, which is copied in whole or in part into the generated code. The license of the original source code with which the AI was trained is not taken into account. It is not clear which license conditions apply and how these can be complied with. For legal reasons, we therefore do not allow AI-generated source code at all.\n\n### Build\n\n*Apus* uses [Maven](https://maven.apache.org/) to build the project. Please use standard Maven commands to build what you need:\n\n| Command          | What it does                                                      |\n|------------------|-------------------------------------------------------------------|\n| `./mvnw`         | compile and run the app                                           |\n| `./mvnw clean`   | cleanup generated files and build artefacts                       |\n| `./mvnw compile` | compile the code without running the tests                        |\n| `./mvnw test`    | compile and run all tests                                         |\n| `./mvnw package` | compile, test, and create a JAR file to run it with Java directly |\n| `./mvnw verify`  | compile, test, package, and run analysis tools                    |\n\nThere is *no need* to run the `install` or `deploy` tasks. They will just run longer, produce unnecessary output, burn energy, and occupy your disk space. [Don't just blindly run mvn clean install...](https://www.andreaseisele.com/posts/mvn-clean-install/)\n\n\u003e [!NOTE]  \n\u003e *Apus* comes with a complete dockerized build for production use. It is **not** recommended to use the self-contained build for development purposes.\n\n### Debugging\n\n#### Command Line\n\nTo run from the command line, run `./mvnw` and open http://localhost:8080 in your browser.\n\n#### Intellij IDEA\n\n- Locate the `Application.java` class in the project view. It is in the `src` folder, under the main package's root.\n- Right-click on the `Application` class\n- Select \"Debug 'Application.main()'\" from the list\n\nAfter the server has started, you can view the UI at http://localhost:8080/ in your browser.\nYou can now also attach breakpoints in code for debugging purposes, by clicking next to a line number in any source file.\n\n#### Eclipse\n\n- Locate the `Application.java` class in the package explorer. It is in `src/main/java`, under the main package.\n- Right-click on the file and select `Debug As` --\u003e `Java Application`.\n\nDo not worry if the debugger breaks at a `SilentExitException`. This is a Spring Boot feature and happens on every startup.\n\nAfter the server has started, you can view it at http://localhost:8080/ in your browser.\nYou can now also attach breakpoints in code for debugging purposes, by clicking next to a line number in any source file.\n\n### Packaging\n\n#### Maven\n\nYou can use [Maven](https://maven.apache.org/) to build *Apus* for production. Just specify the `production` profile. Example:\n\n```shell\n./mvnw clean package -Pproduction\n```\n\nOnce completed successfully, you will find the artifact in the `target` directory. The file is named `apus-VERSION.jar`.\n\n#### Docker\n\nTo create a production build for *Apus* it is highly recommended to use [Docker](https://www.docker.com/) or [Podman](https://podman.io/). *Apus* comes with a complete dockerized self-contained build. You don't need to have Maven or Java installed, [Docker](https://www.docker.com/) or [Podman](https://podman.io/) is enough. The Docker build file contains everything needed, just start a standard Docker build with the following command:\n\n```shell\ndocker build -t apus .\n```\n\nThis might run for a while and will produce a Docker image tagged `apus` on your local system.\n\n### Writing Plugins\n\n#### Event Plugins\n\n*Apus* uses a simple plugin technology to import the agenda of various events. Plugins are currently available for the following events:\n\n| Plugin                     | Supported Events                             |\n|----------------------------|----------------------------------------------|\n| `EventDemoPlugin`          | Creates fake session data for demo purposes. |\n| `DevoxxPlugin`             | Devoxx and Voxxed Days conferences           |\n| `DoagPlugin`               | CloudLand, CyberLand, JavaLand, KI Navigator |\n| `JavaForumStuttgartPlugin` | Java Forum Stuttgart                         |\n| `SessionizePlugin`         | BaselOne, Java Forum Nord                    |\n\n#### Social Plugins\n\n*Apus* uses a simple plugin technology to import posts from various social media services. Plugins are currently available for the following services:\n\n| Plugin             | Supported Services                          |\n|--------------------|---------------------------------------------|\n| `BlueSkyPlugin`    | BlueSky Social                              |\n| `MastodonPlugin`   | Mastodon                                    |\n| `SocialDemoPlugin` | Creates fake social posts for demo purposes |\n\n#### Plugin Development\n\nEveryone is welcome to contribute a plugin themselves. The implementation is very simple. There are two types of plugins: `EventPlugin` and `SocialPlugin`. For a new plugin, a new package is created under `swiss.fihlon.apus.plugin.event` or `swiss.fihlon.apus.plugin.social`, based on the plugin type. The implementation is carried out in this new package. Implement one of these two interfaces depending on the plugin type you want to contribute and annotate the class with `@Service`.\n\nIf your implementation requires a configuration, implement the configuration as a `record` in your new plugin package. Take the configuration of one of the existing plugins as a template. Default settings belong in the file `application.properties` and the corresponding schema is stored in `additional-spring-configuration-metadata.json`. Finally, add the configuration class of your new plugin to the `AppConfig` class and modify this `README.md` accordingly.\n\n## Communication\n\n### Matrix Chat\n\nThere is a channel at Matrix for quick and easy communication. This is publicly accessible for everyone. For developers as well as users. The communication in this chat is to be regarded as short-lived and has no documentary character.\n\nYou can find our Matrix channel here: [@project-apus:ijug.eu](https://matrix.to/#/%23project-apus:ijug.eu)\n\n### GitHub Discussions\n\nWe use the corresponding GitHub function for discussions. The discussions held here are long-lived and divided into categories for the sake of clarity. One important category, for example, is that for questions and answers.\n\nDiscussions on GitHub: https://github.com/McPringle/apus/discussions  \nQuestions and Answers: https://github.com/McPringle/apus/discussions/categories/q-a\n\n## Contributors\n\nSpecial thanks for all these wonderful people who had helped this project so far ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/McPringle\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/1254039?v=4?s=100\" width=\"100px;\" alt=\"Marcus Fihlon\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMarcus Fihlon\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#projectManagement-McPringle\" title=\"Project Management\"\u003e📆\u003c/a\u003e \u003ca href=\"#ideas-McPringle\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e \u003ca href=\"https://github.com/McPringle/apus/commits?author=McPringle\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#design-McPringle\" title=\"Design\"\u003e🎨\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/jcgueriaud1\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/51313578?v=4?s=100\" width=\"100px;\" alt=\"Jean-Christophe Gueriaud\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJean-Christophe Gueriaud\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=jcgueriaud1\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/myyxl\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/22593897?v=4?s=100\" width=\"100px;\" alt=\"Marlon\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMarlon\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/issues?q=author%3Amyyxl\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/tlangdun\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/51236478?v=4?s=100\" width=\"100px;\" alt=\"tlangdun\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003etlangdun\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#tool-tlangdun\" title=\"Tools\"\u003e🔧\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/MarkusBarthlen\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/13293680?v=4?s=100\" width=\"100px;\" alt=\"MarkusBarthlen\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMarkusBarthlen\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#tool-MarkusBarthlen\" title=\"Tools\"\u003e🔧\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/eins78\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/134942?v=4?s=100\" width=\"100px;\" alt=\"Max Albrecht\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMax Albrecht\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#infra-eins78\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e \u003ca href=\"#tool-eins78\" title=\"Tools\"\u003e🔧\u003c/a\u003e \u003ca href=\"#design-eins78\" title=\"Design\"\u003e🎨\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/StefanMallia\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/5004438?v=4?s=100\" width=\"100px;\" alt=\"Stefan Mallia\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eStefan Mallia\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=StefanMallia\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/jzfrank\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/77217626?v=4?s=100\" width=\"100px;\" alt=\"jzfrank\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ejzfrank\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=jzfrank\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/Interactiondesigner\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/17220369?v=4?s=100\" width=\"100px;\" alt=\"Interactiondesigner\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eInteractiondesigner\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#design-Interactiondesigner\" title=\"Design\"\u003e🎨\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/1tchy\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/678713?v=4?s=100\" width=\"100px;\" alt=\"Itchy\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eItchy\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=1tchy\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#design-1tchy\" title=\"Design\"\u003e🎨\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/lokalesnetzwerk\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/35575304?v=4?s=100\" width=\"100px;\" alt=\"Max\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMax\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#design-lokalesnetzwerk\" title=\"Design\"\u003e🎨\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://adrianperez.me/\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/9467708?v=4?s=100\" width=\"100px;\" alt=\"Adrian Perez\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAdrian Perez\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#infra-adpe\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/patlecat\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/422020?v=4?s=100\" width=\"100px;\" alt=\"patlecat\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003epatlecat\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=patlecat\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/haladamateusz\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/26378632?v=4?s=100\" width=\"100px;\" alt=\"Mateusz Halada\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMateusz Halada\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=haladamateusz\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/leem53\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/45996424?v=4?s=100\" width=\"100px;\" alt=\"Lennart\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eLennart\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=leem53\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/RomeoIndiaJulietUniform\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/157402485?v=4?s=100\" width=\"100px;\" alt=\"RIJU MONDAL\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eRIJU MONDAL\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#infra-RomeoIndiaJulietUniform\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/ky0n\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/30866028?v=4?s=100\" width=\"100px;\" alt=\"Hendrik\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eHendrik\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=ky0n\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/uzoltan\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/16861870?v=4?s=100\" width=\"100px;\" alt=\"Zoltán Umlauf\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eZoltán Umlauf\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=uzoltan\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://offline.ch/\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/8600029?v=4?s=100\" width=\"100px;\" alt=\"Tobias Kündig\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eTobias Kündig\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=tobias-kuendig\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"http://www.sandra-parsick.de/\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/976007?v=4?s=100\" width=\"100px;\" alt=\"Sandra Parsick\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eSandra Parsick\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=sparsick\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://aschemann.net/\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/456000?v=4?s=100\" width=\"100px;\" alt=\"Gerd Aschemann\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eGerd Aschemann\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=ascheman\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/johthor\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/2544827?v=4?s=100\" width=\"100px;\" alt=\"Johannes Thorn\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJohannes Thorn\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/McPringle/apus/commits?author=johthor\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\n## Copyright and License\n\n[AGPL License](https://www.gnu.org/licenses/agpl-3.0.de.html)\n\n*Copyright (C) Marcus Fihlon and the individual contributors to **Apus**.*\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcpringle%2Fapus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcpringle%2Fapus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcpringle%2Fapus/lists"}