Giter Site home page Giter Site logo

quiltmc / cozy-discord Goto Github PK

View Code? Open in Web Editor NEW
23.0 6.0 24.0 1.33 MB

Discord bot used for Quilt's day-to-day tasks. Discord avatar based on Feuerfuchs blobfox emoji at https://www.feuerfuchs.dev/en/projects/blobfox-emojis/

License: Other

Kotlin 95.51% Groovy 4.45% Dockerfile 0.04%
cozy discord discord-bot fox kordex kotlin

cozy-discord's Introduction

Cozy: Discord

This repository contains a Discord bot that we make use of to help keep the Quilt community servers running smoothly. Its features include, but are not limited to:

  • A fully-featured suggestions system, with PluralKit support
  • A thread ownership system that allows users to manage and transfer their own threads
  • Moderation tools, such as server and channel locking, mute role permissions syncing, adding staff to threads, and message logging
  • GitHub repository management tools
  • A robust message alerting and filtering system
  • Minecraft snapshot alerting and tracking tools
  • Cross-server ban synchronisation tools
  • Miscellaneous utilities

Most of the features currently implemented within Cozy were designed with Quilt in mind, and haven't been factored out into reusable modules. We do plan to do this at some point, but there's a ways to go yet!

Functionality is split into several modes:

  • dev: Development server tooling, including GitHub management
  • collab: Quilt Community Collab mode, mostly for ban-sharing
  • quilt (default): General community management and user-facing tools
  • showcase: Showcase mode, for allowing servers to cross-post their mod screenshots, updates and releases

Modes are specified via the MODE environment variable - see below for more information on that.

Development Requirements

If you're here to help out, here's what you'll need. Firstly:

  • A JDK, Java 15 or later - if you need one, try Adoptium
  • An IDE suitable for Kotlin and Gradle work
    • IntelliJ IDEA: Community Edition should be plenty
    • Eclipse: Install the latest version of the Kotlin plugin, then go to the Window menu, Preferences, Kotlin, Compiler and make sure you set up the JDK_HOME and JVM target version
  • A MongoDB server: Download and install | Docker | Hosted (there's a free tier)
  • A Discord bot application, created at the developer dashboard. Make sure you turn on all the privileged intents - different modes require different intents!

Setting Up

As a first step, fork this repository, clone your fork, and open it in your IDE, importing the Gradle project. Create a file named .env in the project root (next to files like the build.gradle.kts), and fill it out with your bot's settings. This file should contain KEY=value pairs, without a space around the = and without added quotes:

TOKEN=AAA....
DB_URL=mongodb://localhost:27017/

ENVIRONMENT=dev
# You get the idea.

Required settings:

  • TOKEN: Your Discord bot token, which you can get from the developer dashboard linked above
  • DB_URL: MongoDB database URL - for a local server, you might use mongodb://localhost:27017/ for example

Logging settings:

  • ENVIRONMENT: prod (default) for info logging on SystemErr, dev for debug logging on SystemOut

Settings used by all modes:

  • COMMUNITY_GUILD_ID: ID of your "community" server
  • TOOLCHAIN_GUILD_ID: ID of your "toolchain" server
  • GUILDS: A comma-separated list of guild IDs, if not just the two above
  • COMMUNITY_MODERATOR_ROLE: ID of your "community moderator" role
  • TOOLCHAIN_MODERATOR_ROLE: ID of your "toolchain moderator" role
  • MODERATOR_ROLES: A comma-separated list of moderator role IDs, if not just the two above

Settings used by mode: quilt

  • SUGGESTION_CHANNEL_ID: ID of the channel to use for the suggestions system
  • MESSAGE_LOG_CATEGORIES: A comma-separated list of category IDs to use for message logging

Settings used by mode: dev

  • GITHUB_TOKEN: GitHub auth token, for the GitHub project management commands

Once you've filled out your .env file, you can use the run gradle task to launch the bot. If this is your first run, you'll want to start with the quilt mode as this is the mode that runs the database migrations. After that, feel free to set up and test whichever mode you need to work with.

Conventions and Linting

This repository makes use of detekt, a static analysis tool for Kotlin code. Our formatting rules are contained within detekt.yml, but detekt can't verify everything.

To be specific, proper spacing is important for code readability. If your code is too dense, then we're going to ask you to fix this problem - so try to bear it in mind. Let's see some examples...

Bad

override suspend fun unload() {
	super.unload()
	if (::task.isInitialized) {
		task.cancel()
	}
}
action {
	val channel = channel.asChannel() as ThreadChannel
	val member = user.asMember(guild!!.id)
	val roles = member.roles.toList().map { it.id }
	if (MODERATOR_ROLES.any { it in roles }) {
		targetMessages.forEach { it.pin("Pinned by ${member.tag}") }
		edit { content = "Messages pinned." }
		return@action
	}
	if (channel.ownerId != user.id && threads.isOwner(channel, user) != true) {
		respond { content = "**Error:** This is not your thread." }
		return@action
	}
	targetMessages.forEach { it.pin("Pinned by ${member.tag}") }
	edit { content = "Messages pinned." }
}
action {
	if (this.member?.asMemberOrNull()?.mayManageRole(arguments.role) == true) {
		arguments.targetUser.removeRole(
			arguments.role.id,
			"${this.user.asUserOrNull()?.tag ?: this.user.id} used /team remove"
		)
		respond {
			content = "Successfully removed ${arguments.targetUser.mention} from " +
					"${arguments.role.mention}."
			allowedMentions { }
		}
	} else {
		respond {
			content = "Your team needs to be above ${arguments.role.mention} in order to remove " +
					"anyone from it."
			allowedMentions { }
		}
	}
}

Good

override suspend fun unload() {
	super.unload()

	if (::task.isInitialized) {
		task.cancel()
	}
}
action {
	val channel = channel.asChannel() as ThreadChannel
	val member = user.asMember(guild!!.id)
	val roles = member.roles.toList().map { it.id }

	if (MODERATOR_ROLES.any { it in roles }) {
		targetMessages.forEach { it.pin("Pinned by ${member.tag}") }
		edit { content = "Messages pinned." }

		return@action
	}

	if (channel.ownerId != user.id && threads.isOwner(channel, user) != true) {
		respond { content = "**Error:** This is not your thread." }

		return@action
	}

	targetMessages.forEach { it.pin("Pinned by ${member.tag}") }

	edit { content = "Messages pinned." }
}
action {
	if (this.member?.asMemberOrNull()?.mayManageRole(arguments.role) == true) {
		arguments.targetUser.removeRole(
			arguments.role.id,

			"${this.user.asUserOrNull()?.tag ?: this.user.id} used /team remove"
		)

		respond {
			content = "Successfully removed ${arguments.targetUser.mention} from " +
					"${arguments.role.mention}."

			allowedMentions { }
		}
	} else {
		respond {
			content = "Your team needs to be above ${arguments.role.mention} in order to remove " +
					"anyone from it."

			allowedMentions { }
		}
	}
}

Hopefully these examples help to make things clearer. Group similar types of statements together (variable assignments), separating them from other types (like function calls). If a statement takes up multiple lines, then it probably needs to be separated from any other statements. In general, use your best judgement - extra space is better than not enough space, and detekt will tell you if you go overboard.

cozy-discord's People

Contributors

akarys42 avatar anonymous123-code avatar firstmegagame4 avatar gdude2002 avatar ix0rai avatar jamalam360 avatar noahvdaa avatar nocomment1105 avatar potatopresident avatar scotsguy avatar silverandro avatar simpledemonn avatar southpaw1496 avatar sschr15 avatar theglitch76 avatar

Stargazers

 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  avatar

cozy-discord's Issues

Community role and voting tools

Cozy should be able to run Community Team votes for us, and ideally be able to assign and unassign roles as needed by the process.

The voting process is outlined in RFC 0007. Cozy should be able to keep track of voting community team members, voting timeouts and pass/failure conditions, automatic role assignment (if needed), date of promotion, and so on.

This is potentially a pretty big feature, but we'll see.

Request: Separate log parsing into its own library

Log parsing could be moved to its own library, allowing it to be used in other ways (ex: a log analysis page on Quilt's website, possibly through Kotlin/JS; or even in other communities' bots/other tools)

Partially expands upon #54

More Audit Log reasons

The following should all set the audit log reason to the format of "[username] used [command]"

  • /rename
  • /archive
  • (Un)Pin in thread does it already, maybe unify it though
  • /(un)lock-server only puts user in the internal channel
  • /(un)lock -"-

Relicensing: MPL

Because of the changes proposed in #17, I think it's worth moving to a weak copyleft licence. The Mozilla Public License (2.0) is what Kord Extensions (the Framework Cozy uses) is written under, so I'm relatively familiar with it.

This licence change would largely only affect contributors and people that use forks of this repository. It would require anyone distributing modified source code to retain the MPL licence and attribution, and anyone who distributes binary copies to make their modified source available and link to it (or link to this repo if they aren't modifying it).

The licence change would not affect private use, and anyone developing a bot using Cozy modules as provided would not need to do anything.


Cozy is currently licensed under the Creative Commons Zero licence. This is a public domain dedication, which means that we don't technically need to seek permission to change the licence. That said, I would much rather gather that permission from our contributors regardless, in the interests of transparency and good faith interaction.

For that reason, I'll need that confirmation from the following people:

The following people are not contributors, but I'm aware that they may have projects that fork or otherwise use code from this project. For that reason, I'd also like to request their permission:

If you're happy with this licensing change, please leave a comment to that effect below.

Config Commands

We currently have a partial implementation of config commands. Here's what's done, and what needs doing:

  • Global configuration
    • Database
    • Commands
    • Implementation across the bot
  • Server-level configuration
    • Database
    • Commands
    • Implementation across the bot
  • User-level configuration
    • Database
    • Commands
    • Implementation across the bot

We may need to revisit the scope of some of the config options to make sure everything we need to be configurable is.

Mode: Showcase

The Showcase mode should be worked on soon. It'll need to make use of #3, but work can begin before that.

The Showcase mode comprises all of Cozy's showcase channel functionality. The following list of features must be completed before this mode can be considered ready.

  • Creation of managed threads for showcase posts in all channels (except for -discussion), in line with our polling
  • Management commands for managing the list of external servers that are allowed to add the bot
  • User commands for publishing messages to Quilt's showcase channels from their development server
  • User management commands for setting up auto-publishing
  • Database storage of showcase message metadata to allow for the processing of edits and message removals

Welcome channel management

Cozy needs to be able to manage the welcome channels. This includes:

  • Creating and managing rule embeds
  • Creating and managing general information embeds
  • Thread surfacing embeds: #9
  • Easy updating and reloading
  • Role assignment blocks

We'll likely want to create a block system that can be configured via a URL - so, a server would have a welcome channel configured with the URL to a file (probably in this repo), which contains blocks representing each element that needs to be in that channel.

I envision blocks as Markdown documents with front matter, placed within the same file. A block should have a type and its own configuration options - though we can flesh this issue out later.


Quilt-specific tasks:

  • Information about the toolchain server and how to get there

Invite filtering

Due to the poor implementation of invite filters in Zeppelin (lack of a comprehensive log message, automatic triggering if the invite lookup endpoint gets ratelimited), we would like to get our own invite filter right inside Cozy.

Mode: Ban Sharing

The Ban Sharing mode comprises Cozy's ban-sharing functionality, exclusively for use with other large modding communities that we collaborate with. It needs to contain a number of features, but it's worth noting that none of these features should allow the bot to create bans automatically.

  • Bans from all relevant servers need to be stored in the database for lookup purposes, but we should think about how unbans should be handled by that system
    • Note that this does not mean that other information on server members should be stored - we almost definitely should not do that
  • Management commands for managing the list of external servers that are allowed to add the bot
  • Servers must be able to set up a ban relay channel, with settings including a ban command template (to generate ban commands that can easily be copied and pasted) and a way to ignore bans from specific servers
  • Moderator slash commands for ban relaying configuration and ban lookups based on the database
  • Informational messages on ban history in other servers when a user joins

I created a silly diagram at some point, so here it is:

Cozy Collab


Something that's critical to remember is that, while the above features must all be present and fleshed out, they will ultimately be temporary - someday, it's hoped that this functionality can be extracted into an entirely separate organization, with a proper HTTP API, web interface, the ability to write your own bots that consume it, and so on. This is a massive, massive undertaking, though - and not one that will be worked on any time soon - so Cozy should have a good, reliable system of its own until then.

Move the last remaining `?` command to a slash command

image
Currently, there are still the ?help and ?sync bans commands. The help command no longer has a reason to exist with the slash command info being given when you just type /. Thus, the help command should be removed, and the ban sync command moved to a slash command. (Also, change the Cozy [?] bot name accordingly).

Modularization

Cozy is currently very coupled. Extensions need to be factored out into modules, and made generic enough that they'd be easy to slot into a KordEx bot that isn't intended only for Quilt.

We can likely get away with using Gradle submodules for this pretty easily.

Toolchain assignment system

The toolchain Discord server has a somewhat complex management setup. We need to automate this to make it easier, and I think we can do that with Cozy with a bit of work:

  • Cozy needs to store a mapping of roles, to parent roles that are allowed to assign and remove those roles
  • Assignments need to be logged somewhere the developers and admins can get at

It might be useful to add some kind of internal voting system, but this isn't required for an initial version

Feature request: Log analysis in DMs?

Why?

Cozy is capable of analysing logs in the support channel in Quilt, and some just send logs there for getting its automatic analysis of what's going wrong. Allowing it to be DM'd could be useful for those who uses the automatic analysis for their own use rather than to get support.

Upstream Module Adoption

Hello! It me!

When I was still working on Cozy, some of the modules I created were designed to be used by external projects. As I'm no longer part of Quilt, I'd like to adopt them as mainline, first-party Kord Extensions modules:

  • Tags
  • Welcome Channels

As I contributed the vast majority of the code present in these modules, I believe there should be no issue with this from a licensing standpoint. However, I wanted to make sure that was alright with the people who are now running this project.

Of course, you'll be able to use the adopted modules in Cozy as has always been the case - that's the point!

Migration annotations

The way migrations are done is silly. We should write an annotation processor with KSP that allows us to more easily tag migration functions and have them automatically set up.

Prompt for attachments to be uploaded to mclo.gs

Description

When an uploaded attachment is detected by Cozy, it should answer with the suggestion to upload the logs directly to mclo.gs, with a button to do that automatically. The attachment should also be removed if possible.

Rationale

Uploading to mclo.gs helps out in some areas like not needing to download the file to read it fully and being able to actually help with problems on mobile.

This also been discussed in the Discord suggestion 1049616224717385768.

API

mclo.gs has an API available over at api.mclo.gs.

Implementation requirements

  1. The embed shown is as small as possible, or perhaps even a normal message and can be dismissed
  2. You can opt-out
  3. The privacy concerns are taken into account (clearly stating that click the button will share the logs with Aternos).

Does not recognize methods with `::`

If I use an output from the bot as-is, for example net/minecraft/entity/Entity::getVelocityAffectingPos, it always says "No results found". I have to change the :: to . or /.

Lecture/Infodumping module

After a long discussion in a suggestion on the community server, we devised an idea for a user-managed discussion thread system. A system like this would have the following requirements:

  • The ability to set up a thread to be claimed, or in some channels, set up and claim your own threads
  • The ability for the person that claimed the thread to set up a list of co-lecturers, and initially restrict thread participants to themselves and that list of co-lecturers
  • The ability for the person that claimed the thread to set up a "raise hand to speak" mode, whereupon users not currently participating in the thread would be allowed to request the ability to do so, including the ability for the claimant to list other users who may approve those requests, and the ability to revoke the approval later
  • The ability for the person that claimed the thread to fill up the start of the thread with messages about a given subject, and then use the bot to notify the users watching the thread that they may now interact, explaining what forms of interactions are expected
  • A set of timeouts to prevent threads from being claimed (or sticking around) when they become inactive
  • In the case of self-created threads, the ability for the person that claimed the thread (or their co-lecturers) to temporarily unlock the thread later in order to add extra information to it

A sample thread might look something like this:

  • User A claims a thread for themselves, locking participation and adding User B to the co-lecturers list for that thread, and enabling "raise hand to speak" mode
  • User A and User B begin talking about whatever the subject of the thread is, across multiple messages
  • At some point, User C raises their hand, is approved to ask a question and does so, has the question answered, and has the approval revoked so User A and User B can continue
  • When User A and User B have finished, they open the thread to everyone, specifying that they're looking for a respectful discussion on the positives of the thread topic
  • When the conversation dries up, the thread is automatically locked (if self-created) or set up to be claimed by someone else

This has a few use-cases:

  • A safe way to initiate or take part in info-dumping, something which is relatively common, but is specifically quite important to many neurodivergent people
  • A way to have actual lectures on Discord, without relying on voice channels, and in a way that can be archived long-term
  • Probably other things I haven't thought of yet

Thread Persistence Command

Simple enough: A command that marks a thread as "should not archive", and unarchives it whenever that happens - unless a staff member archives it with /thread archive. Can probably just modify the existing threads collection for this.

Showcase: PluralKit integration

Abstract

Currently, when a user using PluralKit posts a message in the #gallery channel, a thread is created on the user message, which is then deleted, making the thread unaccessible. We should account for it.

Specification

When a user post a message, the bot should wait up to one or two seconds to see if the message gets deleted. If so, the PluralKit API should be called. If the message correctly resolves, the thread should be created, with ownership granted to the user account.

[Feature request] Self timeout with removal of verified role/viewing permissions.

Dear Quilt staff,

I would like to request to be able to do a self-timeout that also revokes our ability to see channel contents.

We're currently taking a break from Quilt due to mental health, and thus would like to be able to revoke our ability to see messages as to not have a reason to interact, without having to fully leave the guild.

This could maybe be implemented as an additional optional argument?

Regards,
Rory&

Log Parsing Issue

Cozy is asking people to replace FLK by QKL when the user is on the Quilt Loader using FAPI. It should not since QKL depends on QSL.

Thread Surfacing

As Discord's "Active Threads" list is staff-only, it'd be nice if Cozy could maintain a message or channel that keeps track of active and new threads, just so that people have an easy way to learn about them.

Devlog publishing tools

Devlogs are currently published via announcements channels into another channel on the community server. Instead of this, we should write a Cozy extension that sends them directly to that channel with an embed customized for each team - this would allow people to follow the full suite of devlog channels on their servers directly.

Automatic Voice Channels

I forgot to make an issue for this, so let's make one now.

As discussed in a Discord suggestion, I believe we can do better with our voice channels. Voice channels are often made use of for a specific purpose, but many people make use of them in different ways - and often not in the ways that the group originally using the channel expects. This can result in users disrupting the conversation or activity, and generally causing problems unintentionally.

I proposed that this is a function of how we handle voice channels, and I think we can work around it by creating a Cozy module to automatically handle voice channels. It should work something like this:

  • User joins a "Lobby" voice channel. Cozy creates a voice channel for them, and moves them into it.
  • The user that created the channel is given access to logged management commands that allow them to set the name, prevent non-staff users from joining them, manage simple permissions, set limits on the number of users allowed to join, change the region, disconnect other users, and so on.
  • Users making use of the channel may use Text-in-Voice to chat without using their microphones.
  • When the channel becomes empty, Cozy temporarily archives it, generates a file containing all of the messages and information sent in the thread, uploads that file to a log channel, and deletes the channel.

Approaching voice channels in a dynamic way like this should result in less general voice disruption and a better way to signpost what a channel is for.

GitHub organization management tooling

Cozy should integrate with GitHub Teams, in order to either assign users to teams that correspond with their Discord roles on the Toolchain server, or to grant users roles based on the teams they're assigned to on GitHub.

This needs fleshing out. Please have a conversation!

/slowmode command

Add a command for mods to get, set and reset the slowmode of a channel

Add Status Messages for Cozy

(Suggestion 928257607582494730)

Cozy should have a status message. It currently looks out of place in the member's list compared to all the other bots:

image

There are two options that I can think of:

  • Have a single message, e.g. Watching over Quilt
  • Have a list of messages that are randomly cycled through, for example:
    • Watching over Quilt
    • Playing Minecraft
    • etc.

The second option could be more fun and interesting, so that one has my personal vote.

Quilt streamer appreciation

Relevant suggestion

Abstract

We want to help push out Quilt streaming content to our users, by temporarily hosting streaming developers.

Rationale

We have a few streamers in the community, periodically streaming Quilt development, whenever it is on Quilt itself or developing a Quilt mod. We want to show appreciation for those people, and help push out their content to more of our users. More details can be found in the suggestion.

We will start with Twitch for now, as our main streamer base is there. We may explore more platforms such as Youtube in the future.

Implementation

After an application through ModMail, the moderators should be able to use a command to assign the "Twitch Streamer" role to a user and store their Twitch user name or user ID in permanent storage1.

Users with the role "Twitch Streamer" should be able to use the /start-stream command and get hoisted with the "Live on Twitch" role. The bot shall periodically call the Twitch API /streams endpoint to check if the user is still live. If not, the "Live on Twitch" role shall be removed.

We cannot automate giving the role at the time of writing, as the Twitch API does not support custom tags. If that would change in the future, we should be looking for the #QuiltMC tag on streams of verified streamers, and remove the need for the command.

Footnotes

  1. This will require updating our privacy policy โ†ฉ

Mixin Targets missing semicolon

When getting yarn mappings for a method, the mixin target is missing the semicolon between the class and the method name. For instance, ?ym TickCriterion.trigger will give the target:

Lnet/minecraft/advancement/criterion/TickCriteriontrigger(Lnet/minecraft/server/network/ServerPlayerEntity;)V
should have a semicolon here ---------------------^

This error does not occur for field mixin targets

move mod incompatibility list to github

the mod incompatibility list should be somewhere other than the forum, for two reasons

  • it's bothersome to submit an update, since only a few people have permission to make edits
  • it complicates parsing

I propose we host it on github, so update can simply be done through pull requests. I think a dedicated repository for this file would be best, so parity issues with the forum could be tracked through github issues.

Filter notes

When adding a filter, we want to be able to know what that is and why it has been added. Filter notes should help us with that.

How do we want to handle existing filters? Do we want to give them an empty note and fill them up later?

Sync timeouts between the two servers

With the new timeout feature reaching GA, we should make sure that a user timed out in one server is also timed out in the other. That should be done using a member edit listener, as it should be fired every time the timeout changes. We should also make sure to account for early un-timeouts.

Link official blog posts for new Minecraft updates

Quite a few community members seem the prefer the official blog posts about Minecraft updates, and keep posting the link to the official blog post in each thread posted by the bot. I think it'd make a lot of sense for Cozy to automatically include this link alongside Quilt's one.

While the blog post URL is not included in the launcher patch notes it can be obtained via the API used to fetch articles for https://www.minecraft.net/en-us/articles. See the URL used by the page, the filters can be adjusted to return fewer unrelated articles.

I'm not sure if this is feasible to implement, but I did not see any discussion on this before from a little searching on this issue tracker and in Discord so I figured I'd suggest it.

Edit: It's also possible to find all posts via the sitemap, might be preferred since it has no pagination.

Thread Ownership

There is currently no central concept of thread ownership. The bot often needs to create managed threads for users, and those users should be able to alter those threads - the suggestions extension does this, but it should be generalized for other extensions.

This will involve adding a Threads collection, and migrating the suggestions threads over to it.

Utility extension doesn't take the community manager role into account

Those lines should be changed to also allow Community Managers to use those commands:

Especially in the case of archiving (when the prevent archiving is on) and transferring ownership, those features aren't available from the Discord client and are required to operate the community.

Suggestion system bugs

Two things we noticed just now

  1. A user submitted a suggestion and two messages were posted
  2. Reopening the suggestion didn't re-add the voting buttons

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.