Giter VIP home page Giter VIP logo

puffery's Introduction

Puffery

An iOS App written in SwiftUI to send push notifications fueled by Siri Shortcuts.

You can follow other's channels and directly receive updates. There is no algorithm deciding wether you should receive notifications or not.

Download on the App Store

Join the public beta on TestFlight.

Do you want to stay up to date with Puffery-dev-builds? There is a channel for that!

Do you need inspiration or help? Head over to our GitHub discussions!

Server-Configuration

env Description Default
DATABASE_URL Connection string for SQL Database. postgres://puffery-test:puffery-test@localhost:5431/puffery-test
REDIS_URL Connection string for Queues. redis://localhost:6378
PUFFERY_IN_PROCESS_JOBS Runs jobs inside the server. false
APNS_KEY_ID Key ID for Auth Key. Only required for push notifications
APNS_TEAM_ID Team ID for Auth Key. Only required for push notifications
APNS_KEY_PATH Path to private auth key for APNS. private/AuthKey_$APNS_KEY_ID.p8
APNS_ENVIRONMENT Path to private auth key for APNS. production
SENDGRID_API_KEY API Key for sending emails. Only required for emails
PUFFERY_STATISTICS_CHANNELS Comma separated list of notify keys to receive stats. []

Run Server

$ cd PufferyServer
$ swift run puffery serve --hostname 0.0.0.0 --auto-migrate

To access your server from the mobile app add 127.0.0.1 local.puffery.app to /etc/hosts and select the Puffery (Local) scheme.

API-Routes

$ cd PufferyServer && swift run puffery routes
+--------+----------------------------------------------+
| GET    | /                                            |
+--------+----------------------------------------------+
| GET    | /hello                                       |
+--------+----------------------------------------------+
| POST   | /api/v1/register                             |
+--------+----------------------------------------------+
| POST   | /api/v1/login                                |
+--------+----------------------------------------------+
| POST   | /api/v1/confirmations/login/:confirmation_id |
+--------+----------------------------------------------+
| POST   | /api/v1/confirmations/email/:confirmation_id |
+--------+----------------------------------------------+
| GET    | /api/v1/profile                              |
+--------+----------------------------------------------+
| PUT    | /api/v1/profile                              |
+--------+----------------------------------------------+
| POST   | /api/v1/devices                              |
+--------+----------------------------------------------+
| PUT    | /api/v1/devices/:device_token                |
+--------+----------------------------------------------+
| POST   | /api/v1/channels                             |
+--------+----------------------------------------------+
| GET    | /api/v1/channels/:subscription_id            |
+--------+----------------------------------------------+
| POST   | /api/v1/channels/:subscription_id            |
+--------+----------------------------------------------+
| DELETE | /api/v1/channels/:subscription_id            |
+--------+----------------------------------------------+
| GET    | /api/v1/channels                             |
+--------+----------------------------------------------+
| GET    | /api/v1/channels/shared                      |
+--------+----------------------------------------------+
| GET    | /api/v1/channels/own                         |
+--------+----------------------------------------------+
| POST   | /api/v1/notify-inbound-email                 |
+--------+----------------------------------------------+
| POST   | /api/v1/notify/:notify_key                   |
+--------+----------------------------------------------+
| POST   | /notify/:notify_key                          |
+--------+----------------------------------------------+
| GET    | /api/v1/channels/messages                    |
+--------+----------------------------------------------+
| GET    | /api/v1/channels/:subscription_id/messages   |
+--------+----------------------------------------------+
| POST   | /api/v1/channels/:subscription_id/messages   |
+--------+----------------------------------------------+
| POST   | /api/v1/channels/subscribe                   |
+--------+----------------------------------------------+

License

Puffery is available under the MIT license.

puffery's People

Contributors

insanelyharsh avatar vknabel avatar yonihemi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

puffery's Issues

Basic offline usage

When using iOS 14 widgets of #43, they will regularly be rendered blank due to network issues.
If we could instead fall back to cached data, this would be a huge improvement.

Of course this should be implemented using the composable architecture. This also requires all requests to be part of TCA.

Maximal 20 Nachrichten anzeigen

Pro Channel maximal die 20 neusten Nachrichten anzeigen.
Bei All die 30 neusten. Dafür sehen Abonnenten auch „alte“ Nachrichten vor Ihrem Abo. (wirkt weniger tot)

Idee: später per InApp Pagination.

Stummschalten von neuen Subscriptions

Auf dem Server soll das Stummschalten von einer Subscription ermöglicht werden.

Dies umfasst:

  • Das Erstellen eines neuen Channels
  • Das Beitreten eines neuen Channels
  • Keine Benachrichtigungen an stumme Subscriptions schicken!

Inbound Email notifications don’t work

It seems like sending push notifications with emails does not work.
After a brief look into the logs, I saw the inbound email service correctly forwarded the mail itself.

But still, the puffery server responded with a 404 Channel not found.

One quick guess: is resolving the email address prefix case sensitive?

How much is the fish?

Ich hätte gerne mehr Fisch - unseren Kugelfisch - im UI. Ich finde schon die Edition mit den x als Augen cool für "Oops da ist etwas schief gelaufen"

Aber momentan besticht das UI eher durch puristisches Design.

Ich finde wir sollten uns dazu bei Zeiten nochmal ein paar Gedanken machen ;)

Apple Watch App

An Apple Watch app is needed. Complications could show the latest news and highlight it accordingly.

Notifications can also display the correct colors.

Die Sache mit dem Puff

Von @martendV #9 (comment)

zu Pff (#9):

Ich hätte gerne ein cooles Naming für eine Notification. Wie bei Twitter der tweet.

Puff ist ein bisschen problematisch denke ich. Wobei man auch bedenken muss, wenn sich einbürgert, dass die App Puffery heißt und die englische Sprache sich bei Nutzern durchsetzt. Das deutsche umgs. Wort Puff vielleicht nicht gesehen wird. Bzw. wenn es gesehen wird könnte es auch gutes Marketing sein :D

Pff wäre eine Alternative, die nicht so schön aussieht, aber mit einem coolen Notification-Sound einhergehen könnte "pffff" -> der Kugelfisch atmet aus

Stille Benachrichtigungen versenden

Für unseren Puffery Dev wäre es sinnvoll zwar Puffs/Messages zu erstellen, aber diese „still“ zuzustellen. Sonst bimmelt es bei allen.

Da sehe ich zwei Lösungen:

  1. Die einfache: der Versender gibt silent: true beim senden an. Keiner bekommt eine Benachrichtigung.
  2. Die komplexere: wir geben ein paar Level vor (zB 3 Stück: Wichtig, Normal, Unwichtig). Jeder Nutzer entscheidet ab wann er benachrichtigt werden will (gar nicht, ab unwichtig, ab normal, nur wichtig).

Vorteil der zweiten Lösung: Urlaub, @codergeek121 nimmt keine oder nur wichtig, @martendV nimmt normal und ich nehme alle.

Optimized iPadOS UI

Currently Puffery isn't optimized for the iPad and feels weird. Maybe there is a way to see more than just one channel at once?

Nested Multipart E-Mails akzeptieren

Bei der Implementierung von #9 gehen nested multipart emails nicht. Dies ist eine Beschränkung von Vapor und benötigt evtl größere Änderungen.

Beispiel: GitHub E-Mail bestätigen E-Mails

Redis takes too much Resources!

CONTAINER ID        NAME                                      CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
f822a7c7870e        puffery-vapor_puffery-queues_1            0.04%               4.168MiB / 985.4MiB   0.42%               103kB / 310kB       73.8MB / 0B         4
2b5252e7ec66        puffery-vapor_inbound-middleware_1        0.00%               15.14MiB / 985.4MiB   1.54%               637kB / 664kB       142MB / 0B          12
a96cb3b9778d        puffery-vapor_puffery_1                   0.01%               6.109MiB / 985.4MiB   0.62%               825kB / 136kB       113MB / 0B          4
b8f0a56f4d15        puffery-vapor_redis_1                     95.68%              274.1MiB / 985.4MiB   27.81%              17.2MB / 194kB      116MB / 8.89MB      17
f0d06a1b27b1        db                                        0.01%               6.594MiB / 985.4MiB   0.67%               49.1kB / 72.6kB     110MB / 1.42MB      8

docker-compose.yml

version: "3.7"
services:
  redis:
    image: redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - ./data/redis:/data
    command: ["redis-server", "--appendonly", "yes"]

Logs

redis_1               | 1:C 14 May 2020 20:19:06.081 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1               | 1:C 14 May 2020 20:19:06.082 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1               | 1:C 14 May 2020 20:19:06.082 # Configuration loaded
redis_1               | 1:M 14 May 2020 20:19:06.083 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
redis_1               | 1:M 14 May 2020 20:19:06.083 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
redis_1               | 1:M 14 May 2020 20:19:06.083 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
redis_1               | 1:M 14 May 2020 20:19:06.085 * Running mode=standalone, port=6379.
redis_1               | 1:M 14 May 2020 20:19:06.085 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
redis_1               | 1:M 14 May 2020 20:19:06.085 # Server initialized
redis_1               | 1:M 14 May 2020 20:19:06.085 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis_1               | 1:M 14 May 2020 20:19:06.085 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
redis_1               | 1:M 14 May 2020 20:19:06.086 * Reading RDB preamble from AOF file...
redis_1               | 1:M 14 May 2020 20:19:06.086 * Reading the remaining AOF tail...
redis_1               | 1:M 14 May 2020 20:19:06.086 * DB loaded from append only file: 0.000 seconds
redis_1               | 1:M 14 May 2020 20:19:06.086 * Ready to accept connections
redis_1               | 1:signal-handler (1589487577) Received SIGTERM scheduling shutdown...
redis_1               | 1:M 14 May 2020 20:19:37.386 # User requested shutdown...
redis_1               | 1:M 14 May 2020 20:19:37.388 * Calling fsync() on the AOF file.
redis_1               | 1:M 14 May 2020 20:19:37.389 # Redis is now ready to exit, bye bye...
redis_1               | 1:C 14 May 2020 20:20:05.953 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1               | 1:C 14 May 2020 20:20:05.953 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1               | 1:C 14 May 2020 20:20:05.953 # Configuration loaded
redis_1               | 1:M 14 May 2020 20:20:05.954 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
redis_1               | 1:M 14 May 2020 20:20:05.954 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
redis_1               | 1:M 14 May 2020 20:20:05.954 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
redis_1               | 1:M 14 May 2020 20:20:05.958 * Running mode=standalone, port=6379.
redis_1               | 1:M 14 May 2020 20:20:05.958 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
redis_1               | 1:M 14 May 2020 20:20:05.958 # Server initialized
redis_1               | 1:M 14 May 2020 20:20:05.958 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis_1               | 1:M 14 May 2020 20:20:05.958 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
redis_1               | 1:M 14 May 2020 20:20:05.958 * Reading RDB preamble from AOF file...
redis_1               | 1:M 14 May 2020 20:20:05.958 * Reading the remaining AOF tail...
redis_1               | 1:M 14 May 2020 20:20:05.958 * DB loaded from append only file: 0.000 seconds
redis_1               | 1:M 14 May 2020 20:20:05.958 * Ready to accept connections
redis_1               | 1:S 14 May 2020 20:25:38.317 * Before turning into a replica, using my master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
redis_1               | 1:S 14 May 2020 20:25:38.324 * REPLICAOF 51.83.171.41:8886 enabled (user request from 'id=5 addr=134.209.57.77:42058 fd=10 name= age=1 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=46 qbuf-free=32722 obl=0 oll=0 omem=0 events=r cmd=slaveof')
redis_1               | 1:S 14 May 2020 20:25:39.205 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:39.205 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:39.229 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:39.254 * Master replied to PING, replication can continue...
redis_1               | 1:S 14 May 2020 20:25:39.307 * Trying a partial resynchronization (request 064d39fd8ac30b1632587e120229ead5298114ab:1).
redis_1               | 1:S 14 May 2020 20:25:39.342 * Full resync from master: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ:1
redis_1               | 1:S 14 May 2020 20:25:39.342 * Discarding previously cached master state.
redis_1               | 1:S 14 May 2020 20:25:39.344 * MASTER <-> REPLICA sync: receiving 55656 bytes from master
redis_1               | 1:S 14 May 2020 20:25:39.413 * MASTER <-> REPLICA sync: Flushing old data
redis_1               | 1:S 14 May 2020 20:25:39.414 * MASTER <-> REPLICA sync: Loading DB in memory
redis_1               | 1:S 14 May 2020 20:25:39.414 # Wrong signature trying to load DB from file
redis_1               | 1:S 14 May 2020 20:25:39.414 # Failed trying to load the MASTER synchronization DB from disk
redis_1               | 1:S 14 May 2020 20:25:39.415 * Background append only file rewriting started by pid 16
redis_1               | 1:S 14 May 2020 20:25:39.439 * AOF rewrite child asks to stop sending diffs.
redis_1               | 16:C 14 May 2020 20:25:39.439 * Parent agreed to stop sending diffs. Finalizing AOF...
redis_1               | 16:C 14 May 2020 20:25:39.440 * Concatenating 0.00 MB of AOF diff received from parent.
redis_1               | 16:C 14 May 2020 20:25:39.440 * SYNC append only file rewrite performed
redis_1               | 16:C 14 May 2020 20:25:39.441 * AOF rewrite: 0 MB of memory used by copy-on-write
redis_1               | 1:S 14 May 2020 20:25:39.507 * Background AOF rewrite terminated with success
redis_1               | 1:S 14 May 2020 20:25:39.507 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
redis_1               | 1:S 14 May 2020 20:25:39.507 * Background AOF rewrite finished successfully
redis_1               | 1:S 14 May 2020 20:25:40.212 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:40.212 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:40.242 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:40.273 # Error condition on socket for SYNC: Broken pipe
redis_1               | 1:S 14 May 2020 20:25:41.217 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:41.217 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:41.242 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:41.270 # Error condition on socket for SYNC: Broken pipe
redis_1               | 1:S 14 May 2020 20:25:42.219 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:42.220 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:42.240 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:42.262 # Error condition on socket for SYNC: Broken pipe
redis_1               | 1:S 14 May 2020 20:25:43.224 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:43.225 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:43.248 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:43.271 # Error condition on socket for SYNC: Broken pipe
redis_1               | 1:S 14 May 2020 20:25:44.228 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:44.229 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:44.248 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:44.268 # Error condition on socket for SYNC: Connection reset by peer
redis_1               | 1:S 14 May 2020 20:25:45.232 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:45.232 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:45.254 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:45.275 # Error reply to PING from master: '-Reading from master: Resource temporarily unavailable'
redis_1               | 1:S 14 May 2020 20:25:46.236 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:46.236 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:46.256 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:46.277 # Error condition on socket for SYNC: Broken pipe
redis_1               | 1:S 14 May 2020 20:25:46.711 * Module 'system' loaded from ./red2.so
redis_1               | 1:S 14 May 2020 20:25:47.239 * Connecting to MASTER 51.83.171.41:8886
redis_1               | 1:S 14 May 2020 20:25:47.239 * MASTER <-> REPLICA sync started
redis_1               | 1:S 14 May 2020 20:25:47.261 * Non blocking connect for SYNC fired the event.
redis_1               | 1:S 14 May 2020 20:25:47.283 # Error condition on socket for SYNC: Connection reset by peer
redis_1               | 1:M 14 May 2020 20:25:47.475 # Setting secondary replication ID to 064d39fd8ac30b1632587e120229ead5298114ab, valid up to offset: 1. New replication ID is 49a489c1cc8e6493a261afee0cda2d4a1de3be8c
redis_1               | 1:M 14 May 2020 20:25:47.476 * MASTER MODE enabled (user request from 'id=5 addr=134.209.57.77:42058 fd=10 name= age=10 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=34 qbuf-free=32734 obl=0 oll=0 omem=0 events=r cmd=slaveof')
redis_1               | 1:M 14 May 2020 20:25:52.705 * Module system unloaded

Alle Siri Intents außer senden entfernen

Das Review wurde abgelehnt. Die Begründung ist allerdings sehr vage formuliert und lässt viel Interpretationsspielraum.

image

Einfachheitshalber sollen Intents außer dem Senden entfernt werden. Dies schließt potentielle Fehlerquellen aus.
Weiters könnte sich Apple an der nahen Namensgebung des Intents bzgl Standardphrasen für das Senden von Nachrichten stören (iMessage, WhatsApp, ...). Hierfür sollten alle restlichen Vorkommen von Nachricht und Message ersetzt werden. Vielleicht Neuigkeit oder Benachrichtigung?

Anschließend soll die App erneut veröffentlicht werden.

Zero Data Design für Liste aller Channels

Aktuell zeigt die App nur einen improvisierten Zero-Data-Screen an.

Der Anwender will wissen, dass es keine Inhalte gibt und will einen Channel erstellen.
Braucht er eventuell eine Anregung?

Einige Kurzbefehle

Shortcuts/Kurzbefehle zum Versenden von Nachrichten und zum Abfragen der Channels.

Weitere können folgen

Entfernen/Löschen von Subscriptions

Subscriptions sollen von Channels entfernt werden können.
Der entsprechende Button dazu existiert bereits in der App.

Wenn keine Subscription mehr auf dem Channel existiert, soll dieser ebenfalls gelöscht werden.
Der Autor wird nicht getrackt und kann den Channel nicht für alle löschen. Subscriptions können nicht verwaltet werden.

4A2273AA-B500-448A-AF02-5EFB76823E8C

iOS 14 Widgets

Create a widget that displays the latest notification and changes it's background accordingly.

Benachrichtigungen lesen

Channels sollten eine Anzahl zeigen, wieviele Nachrichten ungelesen sind.

Entsprechend muss der Client dem Server mitteilen, wenn diese gelesen wurden.
Ein einfacher Timestamp reicht.

MacOS Support

Puffery lacks a macOS app. Though it requires a bit more effort and creativity.
The iPhone layout just won't feel right.

Parts can be shared with #42. We could create a Catalyst app, but I'd prefer a native SwiftUI app.

App Store Connect: Your app "Puffery" (Apple ID: 1508776889 Version: 1.0 Build: 57) has one or more issues

Dear Developer,

We identified one or more issues with a recent delivery for your app, "Puffery" 1.0 (57). Your delivery was successful, but you may wish to correct the following issues in your next delivery:

After you’ve corrected the issues, you can upload a new binary to App Store Connect.

Best regards,

The App Store Team

Alternative App icons

When using dark mode, the white icon feels a bit too bright.

The idea: provide multiple icons and allow the user to select their favorite.

Redesigned Login

Redesign login screen.

Idea: draw a circle with the puffer in it. The waves will be cropped.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.