GithubHelp home page GithubHelp logo

fatchan / jschan Goto Github PK

View Code? Open in Web Editor NEW
31.0 4.0 5.0 2.72 MB

MOVED TO https://gitgud.io/fatchan/jschan

Home Page: https://gitgud.io/fatchan/jschan

License: GNU Affero General Public License v3.0

JavaScript 68.08% CSS 16.37% Shell 0.15% Pug 15.40%
imageboard anonymous nodejs mongodb redis nginx chan image board

jschan's Introduction

jschan's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar fatchan 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

Watchers

 avatar  avatar  avatar  avatar

jschan's Issues

ban preserved post file deletion

currently, posts can be "preserved" and embedded in a ban so that they can be shown on the ban screen, however if the post is also deleted, this will delete the files, and the image will not show in the ban.
by adjusting the preserve option to not delete the file if the post is deleted, there will be issues with stray files because bans automatically expire in the database with an expiry index, but this does not delete the files.
I would rather not have a periodic cleaning of files that are not linked to posts and that would probably be very slow so I need another solution.

Installation issues

Need to fix instructions and make code changes for:

  • npm install before npm run-script setup
  • make captcha folder in gulpfile
  • file upload exceeds max size error in modal (dynamicResponse) and check if size exceeds before uploading the whole damn file (multer/express-file-upload issues, gotta check their git issues)

No caching.

Board pages and thread pages always return a HTTP code 200. I was going to originally report that multiple pages would be served from the same URI (domain/b and domain/b/) and that duplicates cache, but I noticed there is no cache at all.

database design issue

I noticed that you create separate collections for boards. That bad design. It will make harder to add boards and handle boards in general. Even a simple board listing will be much harder because you cant just do a find on a collection that holds all boards.

Alternative Captchas

  • "Faptcha" or other topic-related captcha
  • Motion/Animated Captcha
  • Sound/Audio Captcha
  • Math Captcha

Misc. backend and general todos

-escaping issue with boards customcss
-overboard
-raw html input for global staff posts.
-make captchas and blockbypass checked before file uploads
-improve move action ref tracking and remarkup
-oldschool "file" boards, use table layout for indexes instead of
-desudesutalk (image steganography) support

database query consolidation

Need to replace some Promise.all(x.map(...)) type multi queries with a single query with $or's, or bulkwrites.
Places remaining where this occurs:

  • delete-post model for non-board-specific deletes
  • needsAggregate condition in action handler
  • fetching thread posts in getRecents in db/posts more of a caching issue/improvement to be made

Draft design of URL paths

  1. Named boards should have matching URLs instead of board IDs
  2. Using URL to search board threads would be useful

Design example

  1. boards should be <domain_name>/<board_name>
    • board name should be lower case English alphabet and numbers ONLY
  2. threads should be <domain_name>/<board_name>/<thread_id>.html
    • thread IDs should be octal, decimal, dozenal (duodecimal) or hexadecimal
      • Depending on the ID type, create different regex for digits and prefixes like 0x
    • each board should have unique indexes for people who like to play GETball (e.g. /sp/)
  3. common pages should be <domain_name>/<board_name>/<page>.html
    • pages includes (but not liited to):
      • Rules
      • FAQ/wiki
      • index
      • Catalog
  4. board title search would look like <domain_name>/<board_name>/<keyword>
    • We need to define a good set of keyword search functions
  5. multi-board view would look like <domain_name>/<board_name>+<board_name>
    • The + sign is used to combine different boards
    • The * sign is used for wildcard searching board names
  6. multi-board search would look like <domain_name>/<board_name>+<board_name>/<keyword>
  7. [moderation]
  8. [archiving]

pluralise some things - cosmetic

For example x post(s) omitted should display the plural correctly depending on if the number is > 1, so should make a very simple helper to return the word with an "s" on the end if the number if > 1

8chan-like features

  • User-created boards
  • Group/Community moderation on boards
  • Registered accounts
    • Board Owners (person who owns the board)
    • Board Volunteers (person who helps the BO)
  • Board tags
    • use in board searching
  • Board names (alphanumeric)
    • case insensitive form cyberpunk (used in URLs)
    • display form /CyberPunk/ (as displays)
  • Board announcements a.k.a. "red bar of text on the top of the page"
  • Board links to other boards a.k.a. "Sister/Collaborate boards"
  • Moderation activity
  • board claiming
  • board transfer
  • File support
    • Music (MP3, OGG/OGA/MOGG, OPUS, APE, FLAC, WAV, AAC/M4A/3GP, AIFF/AIF/AIFC, WMA/WMV, ALAC/M4A/CAF, etc.)
    • Video (MP4/M4P/M4V, MKV, WMV, MOV/QT, AVI, FLV, OGV, MPG/MP2/MPEG/MPE/MPV etc.)
    • Images (APNG, WEBP, FLIF, HEIF, AVIF etc.)
    • Ebooks (PDF, EPUB, MOBI, DJVU)
  • GET scripts (for /sp/)
  • Cycled Threads (deletes old posts except OP in order to keep it from getting bumplocked)
    • User-invoked
    • Moderation-invoked (by Owner or Volunteer)
  • Top Stickied Threads (must be Moderation-invoked)
  • Bottom Stickied Threads (must be Moderation-invoked)
  • Wiki/FAQ
  • Board flags (255 flags per board)
  • Board banners
  • Board ranking system
    • Most active boards (based on post count per hour)
    • Most popular boards (based on users per day)
  • Minimum character count per OP
  • Minimum image count per OP
  • archives
    • Custom in-house archives
    • Usage of archive.fo
  • Meta-boards (see https://nerv.8ch.net/tech/cyber/g/sudo/prog/sci/)
  • Word filters
    • Auto word ban (for spam)
    • Word filters/translators (for censorship/obfuscation)
  • Report mechanism
    • Report to local board moderation
    • Report to global moderation
    • Ease of moderation through checking all posts from one ID
    • Ease of moderation through checking all posts with similar names or other markers
  • Video embedding
    • Re-route YouTube videos to invidio.us
  • User ID
    • Unique ID hash for each board (requires a salt for every board)
    • Unique ID hash for each thread (requires a salt for every thread)
    • ID representation as psuedo-names (see http://worrydream.com/tripphrase/)
  • Dice roll like https://app.roll20.net/assets/dengine.js?v=123
  • File boards (see http://boards.4chan.org/f/ and https://8ch.net/tdt/ )

needs client scripts:

  • (you) counter or highlighter
  • Tab icon alerts
    • blinks/changes to alert thread activity
    • Tab title has a number with parentheses indicating number of new posts
    • Tab title has an exclamation point indicating "slid" thread
  • Hide mechanism
    • Hide singular post
    • Hide by ID
    • Hide by Name or Flag
    • Hide by keyword in text

Archive sites css issues

Due to use of css vars and flex, archived pages e.g. archive.today produce bad results. No plans to fix the flex/alignment issues, but for vars (used for colors), there needs to be some kind of fallback that uses default theme colors and gulp concat's it to the start of style.css

suggestion: dropping bcrypt

In my experience, bcrypt is one of the most troublesome dependencies to work with. From installation issues with lynxchan, bcrypt is easily the one that most people had issues with. Node ships with pbkdf2, which despite requiring a bit more of work to be used properly, won't give anyone headaches on installation. Sure there are pros and cons between the both as far as security goes, but pbkdf2 is good enough to be recommended by some authority that I can't remember now about it.

reply and image count update on deletes

decide whether to handle reducing these numbers on post or file deletes, since all actions are handled as batches it would b a complex query to update many different threads and reduce the image and reply count

finish the captcha system

  • delete files for expired captchas (db entries already expire on an index, and are deleted when answered correctly.)
  • move the cookie check to nginx
  • Only expire captcha on successful action

also #1 captcha generation needs improving in helpers/captchagenerate.js

Support hidden services

From a broad perspective would need:
-moderation and ratelimit changes since things can't be IP based
-use bypass ID instead of ip
-captchas from a pool and max amount of uses per captcha, dynamic pool size based on global pph

IOS browser isues

I have no clue how to get a debugger or dev environment for ios safari, but there is some issue (probably javascript related) breaking posting.

Would appreciate help and reports of other browser compatibility problems.
Primarily I test with:

  • Desktop firefox, chromium and palemoon
  • Android firefox and chromium

Settings page internal error

I tried gulp migrate and manually fixing tables like mentioned. It didn't help.

1|chan     |     at Object.exports.renderFile (/var/www/yakuchan.club/html/node_modules/pug/lib/index.js:427:38)
1|chan     |     at Object.exports.renderFile (/var/www/yakuchan.club/html/node_modules/pug/lib/index.js:417:21)
1|chan     |     at View.exports.__express [as engine] (/var/www/yakuchan.club/html/node_modules/pug/lib/index.js:4
64:11)
1|chan     |     at View.render (/var/www/yakuchan.club/html/node_modules/express/lib/view.js:135:8)
1|chan     |     at tryRender (/var/www/yakuchan.club/html/node_modules/express/lib/application.js:640:10)
1|chan     |     at Function.render (/var/www/yakuchan.club/html/node_modules/express/lib/application.js:592:3)
1|chan     |     at ServerResponse.render (/var/www/yakuchan.club/html/node_modules/express/lib/response.js:1012:7)
1|chan     |     at module.exports (/var/www/yakuchan.club/html/models/pages/manage/settings.js:9:3)
1|chan     |     at Layer.handle [as handle_request] (/var/www/yakuchan.club/html/node_modules/express/lib/router/l
ayer.js:95:5)
1|chan     |     at next (/var/www/yakuchan.club/html/node_modules/express/lib/router/route.js:137:13)
1|chan     |     at csrf (/var/www/yakuchan.club/html/node_modules/csurf/index.js:117:5)
1|chan     |     at Layer.handle [as handle_request] (/var/www/yakuchan.club/html/node_modules/express/lib/router/l
ayer.js:95:5)
1|chan     |     at next (/var/www/yakuchan.club/html/node_modules/express/lib/router/route.js:137:13) {
1|chan     |   path: '/var/www/yakuchan.club/html/views/includes/2charisocountries.pug'
1|chan     | }

better captcha generation

currently they are quite hard to read and also i want more random distortion since the wave isnt that great. Ideally this can be done with the gm module, but I'm not totally against uing fork/exec.

If anybody could give some pointers on this I would appreciate.

Frontend features

-(you)'s
-script for embedding video links with warning of external resource
-selective files spoilering
-auto update catalog
-Individual image hiding
-refresh captcha when expired
-more post styling keyboard shortcuts, and add to FAQ
-favorite boards
-move threadstat template into pug and compileclient
-reply box auto show when scrolled down (maybe)
-add webworker to share socket
-rewrite post hiding on frontend
-inline quotes
-(multiple) file upload using URL
-:placeholder-shown and/or :valid and next sibling selector CSS for clean noscript UI
-use similar proxy pattern with node--fetch agent
-node-fetch stream dls
-prevent invalid post action checkbox combinations (disabled attribute and muted class)
-make "view full text" expand that post without opening the thread
-show hidden posts list in settings menu, allow to be cleared
-option to fully hide posts i.e. no transparent stubs
-currently playing video/audio controls

bugs/issues:
-fix captcha field not available if enabled after page load
-xhr onerror "something broke"
-postbox jumping to top/left on resize. make it ride the bottom/right sides
-notifications options: make toggleable per thread, option to only show for (you)'s
-make clicking notification clear it and open the tab/thread
(notifications behaviour generally inconsistent)

Moderation features/board settings

-make the post actions panel more than 1 column for mods since its pretty big now
-modlog link to dismissed/edited
-manually enter ip (or hash substring) to ban
-add warnings, and change "ban message" to "ban/warning message"
-ability to resign from global staff, or from board ownership or moderator
-custom board flags, add to settings page and similar interface to banners page
-change banners tab to 'assets' for banners, customcss and flags images (max assets limit no. and size?)
-record board staff last activity date for inactivity/claims
-option to resign from position as board owner or moderator (would also add to inactive/claims)
-per-board filesize limit
-add traditional filter replacement mode
-prevent file repost per board/thread setting, as an extension to this, r9k mode
-multi-stage options for PPH/TPH triggers, e.g. captcha first, then lock.

Some suggestions

-Board-specific settings page. I don't knwo that's a feature but it gives internal error. Settings can include changing board name/description, adding icons, etc..
-Firefox favicon. I can probably do this myself, but I don't want ot break anything.
-Overboard

action handler delete file/post order

If delete and some other actions are selected together, the logic of the action handler is:
if we are deleting posts, other conflicting actions e.g. sage, sticky, report, unlink files are irrelevant.

However this should not be the case for deleting files because staff may want to delete the post and files immediately, however deleting the post will say the post is deleted, but not remove the files from the server, unless delete_files is used first.
Even in future when pruning of old deduplicated files is implemented, some files may not want to be kept on the server. so the order in the actionhandler needs to be adjusted, putting delete_file before delete posts.

JSON API

need to make read-only JSON API and make it compatible with format of other sites.

Gonna be a major change since i would need to change name of properties in database and references to them elsewhere in code and views. This is because i want to return raw query data from the db as easily as possible without having to change key names before returning.

Contacts

Do you have any one of these? If so, please write them down in the ReadME

  • Discord (preferred)
  • Email
  • Matrix
  • IRC

isBoard already declared

I have completed installation as per instructions on the front page of the project. However I can see there are errors in both chan-error.log and build-worker-error.log. Both have the same syntax error relating to the isBoard variable, which I believe lives in head.pug. I suspect this isn't a problem with head.pug, but is possibly a problem with my environment, but I'm at a loss.

Here you can see an exert of the errors from build-worker-error.log:

You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection:
SyntaxError: Identifier 'isBoard' has already been declared
    at Function (<anonymous>)
    at wrap (/home/admin/jschan/node_modules/pug-runtime/wrap.js:6:10)
    at Object.exports.compile (/home/admin/jschan/node_modules/pug/lib/index.js:287:7)
    at handleTemplateCache (/home/admin/jschan/node_modules/pug/lib/index.js:240:25)
    at Object.exports.renderFile (/home/admin/jschan/node_modules/pug/lib/index.js:452:10)
    at module.exports (/home/admin/jschan/helpers/render.js:14:19)
    at Object.buildHomepage (/home/admin/jschan/helpers/tasks.js:203:22)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
2020-05-30 08:00:00.175: SyntaxError: Identifier 'isBoard' has already been declared
    at Function (<anonymous>)
    at wrap (/home/admin/jschan/node_modules/pug-runtime/wrap.js:6:10)
    at Object.exports.compile (/home/admin/jschan/node_modules/pug/lib/index.js:287:7)
    at handleTemplateCache (/home/admin/jschan/node_modules/pug/lib/index.js:240:25)
    at Object.exports.renderFile (/home/admin/jschan/node_modules/pug/lib/index.js:452:10)
    at module.exports (/home/admin/jschan/helpers/render.js:14:19)
    at Object.buildHomepage (/home/admin/jschan/helpers/tasks.js:203:22)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:97:5) Promise {
  <rejected> SyntaxError: Identifier 'isBoard' has already been declared
      at Function (<anonymous>)
      at wrap (/home/admin/jschan/node_modules/pug-runtime/wrap.js:6:10)
      at Object.exports.compile (/home/admin/jschan/node_modules/pug/lib/index.js:287:7)
      at handleTemplateCache (/home/admin/jschan/node_modules/pug/lib/index.js:240:25)
      at Object.exports.renderFile (/home/admin/jschan/node_modules/pug/lib/index.js:452:10)
      at module.exports (/home/admin/jschan/helpers/render.js:14:19)
      at Object.buildHomepage (/home/admin/jschan/helpers/tasks.js:203:22)
      at runMicrotasks (<anonymous>)
      at processTicksAndRejections (internal/process/task_queues.js:97:5)
}

Exert from chan-error.log

1|chan  | 2020-05-30 08:20:22.264: SyntaxError: Identifier 'isBoard' has already been declared
1|chan  |     at Function (<anonymous>)
1|chan  |     at wrap (/home/admin/jschan/node_modules/pug-runtime/wrap.js:6:10)
1|chan  |     at Object.exports.compile (/home/admin/jschan/node_modules/pug/lib/index.js:287:7)
1|chan  |     at handleTemplateCache (/home/admin/jschan/node_modules/pug/lib/index.js:240:25)
1|chan  |     at Object.exports.renderFile (/home/admin/jschan/node_modules/pug/lib/index.js:452:10)
1|chan  |     at Object.exports.renderFile (/home/admin/jschan/node_modules/pug/lib/index.js:442:21)
1|chan  |     at View.exports.__express [as engine] (/home/admin/jschan/node_modules/pug/lib/index.js:491:11)
1|chan  |     at View.render (/home/admin/jschan/node_modules/express/lib/view.js:135:8)
1|chan  |     at tryRender (/home/admin/jschan/node_modules/express/lib/application.js:640:10)
1|chan  |     at Function.render (/home/admin/jschan/node_modules/express/lib/application.js:592:
     at ServerResponse.render (/home/admin/jschan/node_modules/express/lib/response.js:1012:7)
1|chan  |     at module.exports (/home/admin/jschan/helpers/dynamic.js:11:14)
1|chan  |     at /home/admin/jschan/server.js:113:10
1|chan  |     at Layer.handle_error (/home/admin/jschan/node_modules/express/lib/router/layer.js:71:5)
1|chan  |     at trim_prefix (/home/admin/jschan/node_modules/express/lib/router/index.js:315:13)
1|chan  |     at /home/admin/jschan/node_modules/express/lib/router/index.js:284:7
1|chan  | 2020-05-30 08:20:24.662: SyntaxError: Identifier 'isBoard' has already been declared
1|chan  |     at Function (<anonymous>)
1|chan  |     at wrap (/home/admin/jschan/node_modules/pug-runtime/wrap.js:6:10)
1|chan  |     at Object.exports.compile (/home/admin/jschan/node_modules/pug/lib/index.js:287:7)
1|chan  |     at handleTemplateCache (/home/admin/jschan/node_modules/pug/lib/index.js:240:25)
1|chan  |     at Object.exports.renderFile (/home/admin/jschan/node_modules/pug/lib/index.js:452:10)
1|chan  |     at module.exports (/home/admin/jschan/helpers/render.js:14:19)
1|chan  |     at buildHomepage (/home/admin/jschan/helpers/tasks.js:203:22)
1|chan  |     at processTicksAndRejections (internal/process/task_queues.js:97:5)
1|chan  |     at async module.exports (/home/admin/jschan/models/pages/home.js:9:10)
1|chan  | 2020-05-30 08:20:24.703: SyntaxError: Identifier 'isBoard' has already been declared
1|chan  |     at Function (<anonymous>)
1|chan  |     at wrap (/home/admin/jschan/node_modules/pug-runtime/wrap.js:6:10)
1|chan  |     at Object.exports.compile (/home/admin/jschan/node_modules/pug/lib/index.js:287:7)
1|chan  |     at handleTemplateCache (/home/admin/jschan/node_modules/pug/lib/index.js:240:25)
1|chan  |     at Object.exports.renderFile (/home/admin/jschan/node_modules/pug/lib/index.js:452:10)
1|chan  |     at Object.exports.renderFile (/home/admin/jschan/node_modules/pug/lib/index.js:442:21)
1|chan  |     at View.exports.__express [as engine] (/home/admin/jschan/node_modules/pug/lib/index.js:491:11)
1|chan  |     at View.render (/home/admin/jschan/node_modules/express/lib/view.js:135:8)
1|chan  |     at tryRender (/home/admin/jschan/node_modules/express/lib/application.js:640:10)
1|chan  |     at Function.render (/home/admin/jschan/node_modules/express/lib/application.js:592:3)
1|chan  |     at ServerResponse.render (/home/admin/jschan/node_modules/express/lib/response.js:1012:7)
1|chan  |     at module.exports (/home/admin/jschan/helpers/dynamic.js:11:14)
1|chan  |     at /home/admin/jschan/server.js:113:10
1|chan  |     at Layer.handle_error (/home/admin/jschan/node_modules/express/lib/router/layer.js:71:5)
1|chan  |     at trim_prefix (/home/admin/jschan/node_modules/express/lib/router/index.js:315:13)
1|chan  |     at /home/admin/jschan/node_modules/express/lib/router/index.js:284:7

cross quotes

since quotes rely on thread id being passed to markdown helper to build the url in the link e.g. /board/thread/5#7, when a new thread is made, quotes to another thread will have a url leading to 404 e.g. `/board/thread/#7. i think it will be very inefficient to check for every quote and find the thread, so maybe there is a smarter way to do this?
i could change the way a thread is checked to exist by allowing post IDs within that thread to redirect to the thread itself, but then the url fragment/hash (whatever the #number is called) will be lost and wont take the user to the quote in the page

2 Factor Authentication

This would be useful for both core board and autonomous board management

  • Email + referral email
    • Requires email server (easier)
    • Has well-established flask plugins
    • Could rely on cock.li service (might need negotiation)
  • Mobile Application (e.g. Telegram, RocketChat)+ PIN Code
    • Requires application bot (harder)
    • Do not use Whatsapp, Messenger, Twitter or SMS
    • Might need further refinements on what software to use or not to use

unlinking file from post with no text results in empty post

Not sure how i would handle this. I cant disallow removing images from no-text posts, since the file might still want to be user removed. Perhaps if image being unlinked, add something similar to banmessage, or perhaps, change banmessage to some kind of log of actions on the post? e.g. actions: [], then push bans, edits, file unlinks/deletes, etc to that log and display it on the post so that a no-text post will not be empty if file unlinked.

Websocket authentication

Will see if I can get existing cookies and check session store on websocket connection event. Would allow:

  • live update on global manage page for recent posts
  • show ip/hashes on mod index and catalog, not only on recent posts and reports page
    • also live updated posts over authed websocket would be able to have the ip/hash in them
  • probably more stuff

batch action handling changes

the batch action handler needs to be adjusted to:

  • build larger queries/filters in mongodb for actions that do not conflict e.g. "lock" and "sticky" to reduce hits to database for better efficiency.

Accessing admin account

I looked everywhere but couldn't find admin account credentials or how to setup one. I know this place isn't a forum so this question will terminate itself. Thanks.

separate scripts

make post wipe, board wipe, board creation, indexing, etc have separate scripts that can be called separately. maybe add it to the gulpfile so scripts could be combined for "clean-all" type scripts or something.

db and file deletion issue

need a smart way to handle db error after/before deleting files

e.g. we use "delete post" action

  1. any post images/files are deleted
  2. db query executed to delete post from db
  3. done

however if the db query fails and the post is not deleted for some reason, the file is gone and we cannot recover it

if we reverse the order and remove from db first, if the file remove fails, we now have a file with no reference to it in the database and it becomes difficult to remove the stray files

need to figure out the correct way to handle this.

file deduplication

i guess i use md5? its already returned from file middleware. there might be collissions. gonna research more

should apply to posts and board banners

possible banner size exploit for gifs

Some gifs when identified by GM will return an array of geometry because the resolution changes per frame. Currently only the first frame is checked for correct size, but i imagine a gif with changing geometry could break the size restriction and look weird.

The banner css is set to 300x100 so it wont push anything down the page, but this should still be looked into.

post id text contrast

need a noscript way of making them contrast the background with either black or white text

Theme sets

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.