GithubHelp home page GithubHelp logo

qnikst / haskellnet Goto Github PK

View Code? Open in Web Editor NEW
86.0 5.0 54.0 497 KB

Haskell library which provides client support for POP3, SMTP, and IMAP protocols.

License: Other

Haskell 99.59% Shell 0.41%

haskellnet's Introduction

HaskellNet

Haskell-CI

This package provides client support for the E-mail protocols POP3, SMTP, and IMAP.

Some examples of how to use the library are contained in the example/ directory. You should be able to run them by adjusting the file for your mail server settings and then loading the file in ghci and type 'main'. eg.

ghci -hide-package monads-fd example/smtpMimeMail.hs main

If you encounter problems and want to debug the ghci debugger works well:

:set -fbreak-on-exception :trace main

haskellnet's People

Contributors

amesgen avatar da-x avatar dpwright avatar fegu avatar javran avatar jmuk avatar jtdaugherty avatar lemol avatar mareksuchanek avatar michaelbeaumont avatar mpscholten avatar nh2 avatar parsonsmatt avatar paumr avatar peterbecich avatar phadej avatar ppetr avatar qnikst avatar ryantrinkle avatar scott-fleischman avatar shapr avatar sordina avatar surr avatar thomasjm avatar vincenthz avatar vzaliva avatar wrwills avatar wwmoraes avatar xaverdh avatar ygale 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  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

haskellnet's Issues

`IMAP.fetch` returns empty string

I was previously running v0.6.0.2 and all my IMAP operations were working fine.

Now I have updated to the latest master version and the fetch operation returns empty strings for some reason:

message <- IMAP.fetch connection uid
-- message == ""

The v0.6.0.2 version of this library works fine when connecting to the same mailbox and fetching the same UID, so it seems to be a recent change.

I've tried out multiple commits, and ea7dd9d2c66c82f464e687239befee3e3f283d17 is the last one that works.

When running with the next commit b6fed67 I get an empty fetch result again. I guess it's somehow related to the change there in the lookup' function.

Do not use the now-removed `Network` module

HaskellNet currently uses the function connectTo, from the Network module in the network library, to create its TCP connections.

In the network library, the Network module has long been deprecated. The docs say to use Network.Socket instead. Now the Network module has been removed, beginning with version 3.0.0.0 released in January 2019 (latest version is 3.0.0.1). It will soon become very difficult to compile HaskellNet in any up-to-date Haskell environment.

As an immediate stop-gap, please add a network < 3.0.0.0 constraint to avoid build failures. Then please switch to using Network.Socket (or some other well-maintained networking library) to create the connections.

EDIT: Sorry, the Network module was not actually removed until 3.0.0.0, in January 2019. It's just that the haddocks stopped building in 2.7.0.0. But now it is gone altogether. Version numbers fixed.

Add support for more flexible backend streams

I wanted to use IMAP with TLS and this turned out to be quite complex. (Relates to #7.) So far, I found these solutions:

  • Use threads - one for wrapping a connection into TLS and another for the actual IMAP handling. This is awful.

  • Use MonadIO instead of IO everywhere. I already tried this variant in this branch for IMAP. It seems to work well and I was able to fuse such patched IMAP with network-conduit. The main part looks like this:

    main = runTCPClient (clientSettings 143 (BS.pack "imap.centrum.cz"))
                capabilities
    
    capabilities :: Application IO
    capabilities ad = appSource ad $= f $$ appSink ad
      where
        f :: Conduit BS.ByteString IO ByteString
        f = do
                c <- connectStream conduitBSStream
                capability c >>= liftIO . print
                logout c
    
    type BSPipe m = ConduitM ByteString ByteString m
    
    conduitBSStream :: (Monad m) => BSStreamM (BSPipe m)
    -- implementation ...
    

    Here BSStreamM is a (backward compatible) modification that takes an arbitrary MonadIO instead of IO. This allows us to run the whole code in Conduit instead of IO and so the actions in BSStreamM can be operations constructed using Conduit primitives. (Full example code here).

    This modification adds generality and the only loss is slightly more complex type signatures.

  • Modify HaskellNet so that it's based on conduit instead of IO. I don't have a clear picture yet. The general idea is that connection wouldn't hold a BSStream but a Sink for network output and a ResumableSource for network input. This would allow to plug in any conduit, even a pure one (for testing, for example).

I'm willing to participate on those changes, if we reach some consensus.

Unable to retrieve mail content

Hello,

I'm trying to run example/imap.hs with my gmail account and all I get is a bunch of empty strings for the content of the mails.
Is it normal or not?

Thanks in advance.

Double connection close on exception.

Currently in case of the protocol error we close connection and throw an exception afterwards:

| otherwise -> do
bsClose (bsstream conn)
fail $ "cannot execute command " ++ show cmd ++
", " ++ prettyExpected expectedReplies ++
", " ++ prettyReceived code msg

It means that when we exit from doSMTP* we close connection again:

bracket (connectSMTPPort host port) closeSMTP

-- | close the connection. This function send the QUIT method, so you
-- do not have to QUIT method explicitly.
closeSMTP :: SMTPConnection -> IO ()
closeSMTP (SMTPC conn _) = bsClose conn
{-
I must be being stupid here
I can't seem to be able to catch the exception arising from the
connection already being closed this would be the correct way to do it
but instead we're being naughty above by just closes the connection
without first sending QUIT
closeSMTP c@(SMTPC conn _) =
do sendCommand c QUIT
bsClose conn `catch` \(_ :: IOException) -> return ()
-}

Even we handle exception here, it's not safe to close the same handle twice as it could be allocated for a completely different connection.

SMTP: Better API for sending mail

Currently we have five different functions to send mails. Some functions names looks confusing to their purposes, so I suggest to have the following two functions:

-- 1
sendMailCustom :: From -> [Destination] -> ByteString -> SMTPConnection -> IO ()
  • This is a replacement for the actual sendMail. It is the most low-level function. Need to specify the sender, the recipients and the just rendered ByteString SMTP mail data.
-- 2
sendMail :: Mail -> SMTPConnection -> IO ()
  • A possibility to send a just constructed Mail data.

And, we may have more high-levels functions with specifics names such as to send plain text mail and to send mime mail or to just construct them:

sendPlainTextMail :: From -> [Destination] -> Subject -> Text -> SMTPConnection -> IO ()
plainTextMail :: From -> [Destination] -> Subject -> Text -> Mail
  • This aim to be the simplest way to send mail. It may be called sendSimpleMail.
sendMimeMail :: From -> [Destination] -> Subject -> [Part] -> SMTPConnection -> IO ()
mimeMail :: From -> [Destination] -> Subject -> [Part] -> Mail
  • Example:
from = From "Me" "[email protected]"
tos   = [To "Gato" "gato@...", justEmailTo "agua@...", Cc "Nada" "nada@...", justEmailBcc "bcc@..."]
bsAttachment = InMemoryAttachment "applicaction/octet-stream" "simple.tar.gz" someBS
fileAttachment = FileAttachment "text/js" "/path/to/file.js"
sendMimeMail from tos "Subject" [PlainTextPart "Hello. Bye!", HtmlPart "<html>...", bsAttachment, fileAttachment, ...] conn

message does not meet IPv6 sending guidelines regarding PTR

When using the restricted gmail SMTP server (as described here) I get this:

user error (cannot execute command DATA "From: <[email protected]>\nTo: <[email protected]>\nSubject: Network.HaskellNet.SMTP Test :)\nMIME-Version: 1.0\nContent-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: quoted-printable\n\nHello world!", expected reply code 250, but received 550 5.7.1 [2a02:8109:8940:9de0:68b1:7de4:7eff:e76c] Our system has detected that
5.7.1 this message does not meet IPv6 sending guidelines regarding PTR
5.7.1 records and authentication. Please review
5.7.1  https://support.google.com/mail/?p=IPv6AuthError for more information
5.7.1 . b10si351447edj.250 - gsmtp

From the guideline, which is linked in the error:

The sending IP must have a PTR record (i.e., a reverse DNS of the sending IP) and it should match the IP obtained via the forward DNS resolution of the hostname specified in the PTR record. Otherwise, mail will be marked as spam or possibly rejected.

Keyword Flag parsing is incorrect

The keyword flag parser in Network.HaskellNet.IMAP.Parsers will parse keywords preceded by "", but according to RFC 3501 section 2.3.2., keywords do not start with slashes. (The parser does have an alternative case for keyword flags in the absence of a slash, so the specified behavior is supported.)

Bump version to > 0.5.1 and upload to hackage

Thanks to great work by @phadej we now support GHC 8.8.1 and modern versions of network. Yay! But the version number in the cabal file is still the same as before, 0.5.1, and none of this is available on Hackage. Could you please just bump the version and upload? Thanks!

Bug: fetchHeaderFields and fetchHeaderFieldsNot are broken

When I call fetchHeaderFields with an hs argument like ["Date"] I get "user error (BAD: Could not parse command)". Comparing the implementation of fetchHeaderFields

fetchHeaderFields :: IMAPConnection
                  -> UID -> [String] -> IO ByteString
fetchHeaderFields conn uid hs =
    do lst <- fetchByString conn uid ("BODY[HEADER.FIELDS "++unwords hs++"]")
       return $ maybe BS.empty BS.pack $
              lookup' ("BODY[HEADER.FIELDS "++unwords hs++"]") lst

to the spec in RFC 9501

    header-list     = "(" header-fld-name *(SP header-fld-name) ")"
    section-msgtext = "HEADER" /
                     "HEADER.FIELDS" [".NOT"] SP header-list /
                     "TEXT"
                       ; top-level or MESSAGE/RFC822 or
                       ; MESSAGE/GLOBAL part

it appears that the problem is that we need to add parentheses around unwords hs, e.g. "("++unwords hs++")".

However, I can simulate this fix by passing an hs argument like ["(Date)"], which eliminates the error, but results in empty bytestrings being returned. Since I'm pretty sure the Date header is present in all the emails I'm querying, it seems the problem is deeper than this.

POP3 Module incorrectly removes whitespace

The POP3 Module removes whitespace from the beginning and end of every line in the response using the strip function in responseML in POP3.hs

According to RFC 2822 however, whitespace at the beginning of a line is used to mark a continuation of the previous line. The behavior of HaskellNet breaks MIME parsers.

IMAP's `fetch` results with extreme memory usage and computer freezing.

I have written the following function using the HaskellNet and HaskellNet-SSL libraries.
Using the "fetch" function results with memory usage blowing up and extreme swapping.
What am I doing wrong? Might it be a bug?

import qualified Data.ByteString.Char8       as SB8
import           Network.HaskellNet.IMAP
import           Network.HaskellNet.IMAP.SSL

main = retrieveCSV

retrieveCSV = do
    con <- connectIMAPSSL imapServer
    login con username password
    select con mailbox
    lastMsg <- last <$> search con [ALLs]
    -- this works:
    _ <- fetchHeader con lastMsg
    -- this works:
    _ <- fetchSize con lastMsg
    -- this blows up in memory usage:
    _ <- fetch con lastMsg
    undefined
    where
    imapServer = "imap.gmail.com"
    username   = "example" -- imagine this username is real
    password   = "example" -- imagine this password is real
    mailbox    = "example" -- imagine this mailbox is real

Mailbox name is not unquoted

The response to IMAP LIST command may have the mailbox name quoted or not. When the mailbox name is parsed, the quotes should be removed.

Output from s_client:

* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\Noselect \HasChildren) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\Important \HasNoChildren) "/" "[Gmail]/Important"
* LIST (\Sent \HasNoChildren) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\Trash \HasNoChildren) "/" "[Gmail]/Trash"

Output from printing the result of list (note the escaped double quotes):

([OtherAttr "HasNoChildren"],"\"INBOX\"")
([Noselect,OtherAttr "HasChildren"],"\"[Gmail]\"")
([OtherAttr "All",OtherAttr "HasNoChildren"],"\"[Gmail]/All Mail\"")
([OtherAttr "Drafts",OtherAttr "HasNoChildren"],"\"[Gmail]/Drafts\"")
([OtherAttr "Important",OtherAttr "HasNoChildren"],"\"[Gmail]/Important\"")
([OtherAttr "Sent",OtherAttr "HasNoChildren"],"\"[Gmail]/Sent Mail\"")
([OtherAttr "HasNoChildren",OtherAttr "Junk"],"\"[Gmail]/Spam\"")
([OtherAttr "Flagged",OtherAttr "HasNoChildren"],"\"[Gmail]/Starred\"")
([OtherAttr "Trash",OtherAttr "HasNoChildren"],"\"[Gmail]/Trash\"")

illegal server response on APPEND

APPEND command gives an error on Gmail server response. Here is the session log:

HaskellNet-SSL SEND: "000002 APPEND CMU   {3152}"
HaskellNet-SSL SEND: "\r\n"
HaskellNet-SSL RECV: "+ go ahead\r"
user error (illegal server response)

Other commands (LOGIN, LIST) worked fine.

Version info:

HaskellNet 0.5.1

Build using new Network Package

Hi,
This library depends on older Network Module.I am getting build errors while installing on my windows machine. Can we have a newer version of this library where it is using network-3...* ?

SELECT does not quote the mailbox name

Perhaps an example would be the clearest:

HaskellNet-SSL RECV: "* OK Gimap ready for requests from 118.189.6.87 ly2mb150532476pab\r"
HaskellNet-SSL SEND: "000000 LOGIN \"[email protected]\" \"test\""
HaskellNet-SSL SEND: "\r\n"
HaskellNet-SSL RECV: "* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS COMPRESS=DEFLATE ENABLE MOVE CONDSTORE ESEARCH UTF8=ACCEPT\r"
HaskellNet-SSL RECV: "000000 OK [email protected] authenticated (Success)\r"
HaskellNet-SSL SEND: "000001 SELECT [Gmail]/All Mail"
HaskellNet-SSL SEND: "\r\n"
HaskellNet-SSL RECV: "000001 BAD Could not parse command\r"
"doh I'm logging out already"
HaskellNet-SSL SEND: "a0001 LOGOUT"
HaskellNet-SSL SEND: "\r\n"
user error (BAD: Could not parse command)

I'll try to dig around and come up with a patch. Any pointer is appreciated.

Error: nodename nor servname provided, or not known

I'm trying to connect to google SMTP, but always get an exception:

Network.Socket.getAddrInfo: does not exist (nodename nor servname provided, or not known)

I just copied the example6 and example7 functions and set the settings as follows:

smtpServer       = "smtp.google.com"
port         = toEnum 587 -- also tried with 465
username     = "[email protected]"
password     = "mypassword"
authType     = LOGIN
from         = "[email protected]"
to           = "[email protected]"
subject      = "Network.HaskellNet.SMTP Test :)"
plainBody    = "Hello world!"
htmlBody     = "<html><head></head><body><h1>Hello <i>world!</i></h1></body></html>"
attachments  = [] -- example [("application/octet-stream", "/path/to/file1.tar.gz), ("application/pdf", "/path/to/file2.pdf")]

What am I doing wrong? (It's definitely using the smtpServer variable)

Stacktrace:


*** Exception (reporting due to +RTS -xc): (THUNK_1_0), stack trace: 
  Network.Socket.getAddrInfo.\.\.\.\,
  called from Network.Socket.getAddrInfo.\.\.\,
  called from Network.Socket.getAddrInfo.\.\,
  called from Network.Socket.getAddrInfo.\,
  called from Network.Socket.getAddrInfo,
  called from Network.connect',
  called from Network.connectTo,
  called from Network.HaskellNet.SMTP.connectSMTPPort,
  called from Main.example7,
  called from Servant.Server.Internal.Handler.liftIO,
  called from Main.myFunction,
...
  called from Network.Wai.Handler.Warp.Run.runSettings.\,
  called from Network.Wai.Handler.Warp.Run.runSettings,
  called from Network.Wai.Handler.Warp.Run.run,
  called from Main.main
Network.Socket.getAddrInfo: does not exist (nodename nor servname provided, or not known) 

Error on append (when not error were reported)

I am trying to append, a message followed by search and get an exception, while IMAP interaction seems to be perfectly normal:

HaskellNet-SSL SEND: "000003 APPEND CMU (\\Seen) \"02-Sep-2020 15:42:58 +0000\" {41232}"
HaskellNet-SSL SEND: "\r\n"
HaskellNet-SSL RECV: "+ go ahead\r"
HaskellNet-SSL SEND: "..."
....
HaskellNet-SSL SEND: ""
HaskellNet-SSL SEND: "\r\n"
HaskellNet-SSL RECV: "* 286 EXPUNGE\r"
HaskellNet-SSL RECV: "* 286 EXISTS\r"
HaskellNet-SSL RECV: "000003 OK [APPENDUID 646710037 324] (Success)\r"
HaskellNet-SSL SEND: "000004 UID SEARCH HEADER Message-ID <CA+ovoGab90ML0kuEK9pfq_a-Pp=ugP1m=C76mjooeMUVKXQmAA@mail.gmail.com>"
HaskellNet-SSL SEND: "\r\n"
HaskellNet-SSL RECV: "* SEARCH 319 324\r"
HaskellNet-SSL RECV: "000004 OK SEARCH completed (Success)\r"
my-exe: IMAPAppendError

Interestingly it works well on other messages and always fails on this one. I suspect it has something to do
with EXPUNGE/EXISTS reponses.

Every fetch returns `*** Exception: expecting either ' ' or ')'`

When connecting to an imap server hosted on outlook.office365.com, every call to fetch in every mailbox returns:

*** Exception: 0000XX:YYY:1: expecting either ' ' or ')'

CallStack (from HasCallStack):
  error, called at src/Network/HaskellNet/IMAP/Parsers.hs:30:38 in HaskellNet-0.5.1-1UL2pVgbYWV1eovDMsu3A7:Network.HaskellNet.IMAP.Parsers

Check if we need to remove retries in tryCommand

We retry each command several times. Such code always look very suspicious and it seems as it hides potential
problems. I've tried to follow the reasons for adding that, but it was a part of a big commit:

024f647

So currently I'm in doubts: removing it will make code cleaner but it could be possible that some systems depend on it and may fail.

Show instance for Flags is incorrect

According to section 2.3.2. Flags Message Attribute of RFC 3501, keyword flags do not begin with "", but the Show instance of Flag in Network.HaskellNet.IMAP shows keywords prefixed with "". At first glance I don't see how this will cause any problems, but it's misleading regardless.

HaskellNet fails when trying to install using Cabal and GHC 7.4.2 on Windows

I am trying to use HaskellNet, but it fails when Cabal starts building it with the following error:

Downloading HaskellNet-0.3...
Configuring HaskellNet-0.3...
Building HaskellNet-0.3...
Preprocessing library HaskellNet-0.3...

src\Network\HaskellNet\IMAP.hs:1:1:
Ambiguous module name `Prelude':
it was found in multiple packages: base haskell98-2.0.0.1
cabal.exe: Error: some packages failed to install:
HaskellNet-0.3 failed during the building phase. The exception was:
ExitFailure 1

Old bug report

From Dmitry Olshansky [email protected]:

I send mail using package HaskellNet(doSMTP, sendMimeMail) in this way:

doSMTP smtpServer $ sendMimeMail to from subj mempty (LT.pack $ mess) []

For SMTP HaskellNet use mime-mail package with QuotablePrintable format. But sometimes additional symbols '=' are appearing there. If I change format to Base64 all is correct.

Michael Snoyman found that:

HaskellNet. In Network.HaskellNet.SMTP, the line

  mapM_ sendLine $ BS.lines dat ++ [BS.pack "."]

combined with

where sendLine l = bsPutCrLf conn l

most likely adds an additional CR in the stream. I think the correct
code should either send the data verbatim or, if you really want to
break it up into lines and ensure CRLF for every line ending, you need
to strip trailing CRs from after the cal to BS.lines (I ran into this
exact bug once). Basically:

bsLines = map stripCR . BS.lines
stripCR x = if not (BS.null x) && BS.last x == 13 then BS.init x else x

Could you check and correct that?

Dependency base64-string gives wrong results

Recently I discovered that SMTP logins were failing for me. The reason turned out to be a buggy base64 encoding. The encode function of the base64-string package version 0.2 has the following behavior:

encode "d36e9aaabaa50295b02a1ca3e1b77bef-c27bf672-1e1d6387"

Gives "ZDM2ZTlhYWFiYWE1MDI5NWIwMmExY2EzZTFiNzdiZWYtYzI3YmY2NzItMWUxZDYz\nODc="

Expected
"ZDM2ZTlhYWFiYWE1MDI5NWIwMmExY2EzZTFiNzdiZWYtYzI3YmY2NzItMWUxZDYzODc="
as given by any other base64 encoder I have tried (for example this one https://www.base64encode.org/)

Tested on Ubuntu, and inside docker.
I am using the stack resolver 13.14 with HaskellNet 0.5.1

To be clear: The actual bug is in the dependency base64-string not HaskellNet itself. But HaskellNet should probably switch to use something that base64 encodes correctly in this use case.

Parse error on Office365 accounts

At the end of a FETCH reply, just before the terminating ), Office365 accounts have an added line of the format UID nn FLAGS (\Seen). This results in a parse error: expected space or ).

Example very last few lines of reply which does not fail:

\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n)\r\n000004 OK FETCH completed.\r\n

Example very last few lines of Office365 reply which fails:

\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nUID 12 FLAGS (\Seen))\r\n000004 OK FETCH completed.\r\n

SMTP with TLS documentation is inadaquate

I am raising this issue just to point out that it is not clear from the documentation how to use SMTP with TLS. Since, most commercial third-party SMTP services like gmail use TLS, I think it is important for the users to know how to do that using HaskellNet. Let me know whether the support is already there. I would love to chime in and work on this issue.

Make use of toStrict from Data.ByteString.Lazy ?

Is there a reason why lazyToStrict in Network/HaskellNet/SMTP.hs does not simply use toStrict from Data.ByteString.Lazy ? As far as I can see, toStrict it is meant exactly for this purpose, and it does seem to help performance a bit (disclaimer: I did not do any real benchmarks, just judging from my use case).

Suggestion: Wrap handlers into a custom monad.

Currently, IMAP (and the other modules) use a handle, which is used in every command. I guess most of the time one will use only IMAP/SMTP/... oprations, only interleaved with some IO. Passing the handle around all the time seems inconvenient.

The suggestion is to create a new monad (under the MonadIO refactoring it would even be a monad transformer) that would capture operations required for manipulation of the handle. So instead of

capability conn ...
login conn ...
liftIO ...
list conn ...
liftIO ...
logout conn ...
close conn ...

one would do

newHandle <- runIMAP oldHandle $ do
    capability ...
    login ...
    liftIO ...
    list ...
    liftIO ...
    logout ...
    close ...

or just

runIMAPConnect hostname port $ do
    ...

or

runIMAPStream stream $ do
    ...

Drawback:

  • Changes the API.
  • Requires replacing an old handle with a new one when using multiple separate runIMAPs.

Further advantages:

  • This would make the operations pure, without relying on any IO. Useful for testing.

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.