Very small optimizations and first upload

This commit is contained in:
nipos 2018-02-26 17:56:23 +01:00
parent fcf58d31a0
commit 6afd23142e
89 changed files with 16607 additions and 2 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
config.ini
.ftpconfig

View File

@ -1,2 +1,62 @@
# halcyon
A Mastodon web client that looks like Twitter - Demo at https://halcyon.cybre.space
# Halcyon for Mastodon
A Mastodon web client that looks like Twitter
>The original author of this genious piece of software was inactive for a while and then shut down his demo instance and deleted this repository. I love Halcyon, it's the thing which makes Mastodon the best social network in the world. I took the Code from the Halcyon fork of cybre.space which still works but doesn't seem to get updates, too. I uploaded it here to make the original link work again and don't link into the big nothing. I'm working on much other stuff, too, what is why I won't be very active here in the near future but I do things which are required to keep it working and if I have much more time, I already know some nice features which could come. As for now I don't have an own demo instance but the one from cybre.space works pretty good and when I changed many things, I will open an own one.
<img src="https://halcyon.cybre.space/login/assets/images/preview0.png">
## Demo
https://halcyon.cybre.space/
Please note that this demo is not owned by me and I have not control over it. Currently I'm not providing an own demo so I link to that one. That may change in future.
## Features
- Twitter like UI, familiar interface.
- Able to use on all instances.
- No tracking, No ads.
## Requirement
- Apache
- PHP
- MySQL
## Setup
I didn't prepared setup script so you have to setup manually...
### PDO MySQL
After installed PHP, run this.
```bash
sudo pecl install pdo_mysql
sudo vi php.ini
```
change to this
```php.ini
extension=mysqli.so
extension=pdo_mysql.so
```
### MySQL
After installed MySQL, create a user, run this.
```sql
CREATE DATABASE DATABASE_NAME DEFAULT CHARACTER SET utf8;
CREATE TABLE DATABASE_NAME.instances(domain varchar(261), client_id varchar(64), client_secret varchar(64));
```
and make file `/config.ini` like this
```config.ini
~~~ line 3 ~~~
api_client_name = <APP NAME>
api_client_website = <APP WEBSITE LINK>
~~~ line 8 ~~~
db_host = <DATABASE HOST DOMAIN>
db_user = <DATABASE USERNAME>
db_pass = <DATABASE PASSWORD>
db_name = <DATABASE NAME>
```
## Credits
- [Kirschn/mastodon.js](https://github.com/Kirschn/mastodon.js)
- [yks118/Mastodon-api-php](https://github.com/yks118/Mastodon-api-php)

17
Roadmap.md Normal file
View File

@ -0,0 +1,17 @@
# Roadmap
These plans may change if I think something else is more important or if I consider something here as not that important!
## Next version
- [x] Oauth login
- [x] Support responsive
- [ ] Add meida attachment's next/prev button
## Future
- [ ] Multi account
- [ ] Emoji picker
- [ ] Native profile edit
- [ ] Dark theme
- [ ] Translation support
- [ ] Mobile devices support
- [ ] PHP PSR4 / Autoload

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 KwangSeon Yun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,970 @@
<?php
// defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Class Mastodon_api
*
* PHP version 7.1
*
* Mastodon https://mastodon.social/
* API LIST https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md
*
* @author KwangSeon Yun <middleyks@hanmail.net>
* @copyright KwangSeon Yun
* @license https://raw.githubusercontent.com/yks118/Mastodon-api-php/master/LICENSE MIT License
* @link https://github.com/yks118/Mastodon-api-php
*/
class Mastodon_api {
private $mastodon_url = '';
private $client_id = '';
private $client_secret = '';
private $token = array();
private $scopes = array();
public function __construct () {}
public function __destruct () {}
/**
* _post
*
* curl post
*
* @param string $url
* @param array $data
*
* @return array $response
*/
private function _post ($url,$data = array()) {
$parameters = array();
$parameters[CURLOPT_POST] = 1;
// set access_token
if (isset($this->token['access_token'])) {
$data['access_token'] = $this->token['access_token'];
}
if (count($data)) {
$parameters[CURLOPT_POSTFIELDS] = http_build_query($data);
}
$url = $this->mastodon_url.$url;
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* _get
*
* @param string $url
* @param array $data
*
* @return array $response
*/
private function _get ($url,$data = array()) {
$parameters = array();
// set authorization bearer
if (isset($this->token['access_token'])) {
$authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
$parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
}
$url = $this->mastodon_url.$url;
if (count($data)) {
$url .= '?'.http_build_query($data);
}
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* _patch
*
* @param string $url
* @param array $data
*
* @return array $parameters
*/
private function _patch ($url,$data = array()) {
$parameters = array();
$parameters[CURLOPT_CUSTOMREQUEST] = 'PATCH';
// set authorization bearer
if (isset($this->token['access_token'])) {
$authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
$parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
}
if (count($data)) {
$parameters[CURLOPT_POSTFIELDS] = json_encode($data);
}
$url = $this->mastodon_url.$url;
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* _delete
*
* @param string $url
*
* @return array $response
*/
private function _delete ($url) {
$parameters = array();
$parameters[CURLOPT_CUSTOMREQUEST] = 'DELETE';
// set authorization bearer
if (isset($this->token['access_token'])) {
$authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
$parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
}
$url = $this->mastodon_url.$url;
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* get_content_curl
*
* @param string $url
* @param array $parameters
*
* @return array $data
*/
protected function get_content_curl ($url,$parameters = array()) {
$data = array();
// set CURLOPT_USERAGENT
if (!isset($parameters[CURLOPT_USERAGENT])) {
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$parameters[CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT'];
} else {
// default IE11
$parameters[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko';
}
}
// check curl_init
if (function_exists('curl_init')) {
$ch = curl_init();
// url 설정
curl_setopt($ch,CURLOPT_URL,$url);
foreach ($parameters as $key => $value) {
curl_setopt($ch,$key,$value);
}
// https
if (!isset($parameters[CURLOPT_SSL_VERIFYPEER])) {
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
}
if (!isset($parameters[CURLOPT_SSLVERSION])) {
curl_setopt($ch,CURLOPT_SSLVERSION,6);
}
// no header
if (!isset($parameters[CURLOPT_HEADER])) {
curl_setopt($ch,CURLOPT_HEADER,0);
}
// POST / GET (default : GET)
if (!isset($parameters[CURLOPT_POST]) && !isset($parameters[CURLOPT_CUSTOMREQUEST])) {
curl_setopt($ch,CURLOPT_POST,0);
}
// response get php value
if (!isset($parameters[CURLOPT_RETURNTRANSFER])) {
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
}
// HTTP2
if (!isset($parameters[CURLOPT_HTTP_VERSION])) {
curl_setopt($ch,CURLOPT_HTTP_VERSION,3);
}
if (!isset($parameters[CURLINFO_HEADER_OUT])) {
curl_setopt($ch,CURLINFO_HEADER_OUT,TRUE);
}
$data['html'] = json_decode(curl_exec($ch),true);
$data['response'] = curl_getinfo($ch);
curl_close($ch);
}
return $data;
}
/**
* set_url
*
* @param string $path
*/
public function set_url ($path) {
$this->mastodon_url = $path;
}
/**
* set_client
*
* @param string $id
* @param string $secret
*/
public function set_client ($id,$secret) {
$this->client_id = $id;
$this->client_secret = $secret;
}
/**
* set_token
*
* @param string $token
* @param string $type
*/
public function set_token ($token,$type) {
$this->token['access_token'] = $token;
$this->token['token_type'] = $type;
}
/**
* set_scopes
*
* @param array $scopes read / write / follow
*/
public function set_scopes ($scopes) {
$this->scopes = $scopes;
}
/**
* create_app
*
* @param string $client_name
* @param array $scopes read / write / follow
* @param string $redirect_uris
* @param string $website
*
* @return array $response
* int $response['id']
* string $response['redirect_uri']
* string $response['client_id']
* string $response['client_secret']
*/
public function create_app ($client_name,$scopes = array(),$redirect_uris = '',$website = '') {
$parameters = array();
if (count($scopes) == 0) {
if (count($this->scopes) == 0) {
$scopes = array('read','write','follow');
} else {
$scopes = $this->scopes;
}
}
$parameters['client_name'] = $client_name;
$parameters['scopes'] = implode(' ',$scopes);
if (empty($redirect_uris)) {
$parameters['redirect_uris'] = 'urn:ietf:wg:oauth:2.0:oob';
} else {
$parameters['redirect_uris'] = $redirect_uris;
}
if ($website) {
$parameters['website'] = $website;
}
$response = $this->_post('/api/v1/apps',$parameters);
if (isset($response['html']['client_id'])) {
$this->client_id = $response['html']['client_id'];
$this->client_secret = $response['html']['client_secret'];
}
return $response;
}
/**
* login
*
* @param string $id E-mail Address
* @param string $password Password
*
* @return array $response
* string $response['access_token']
* string $response['token_type'] bearer
* string $response['scope'] read
* int $response['created_at'] time
*/
public function login ($id,$password) {
$parameters = array();
$parameters['client_id'] = $this->client_id;
$parameters['client_secret'] = $this->client_secret;
$parameters['grant_type'] = 'password';
$parameters['username'] = $id;
$parameters['password'] = $password;
if (count($this->scopes) == 0) {
$parameters['scope'] = implode(' ',array('read','write','follow'));
} else {
$parameters['scope'] = implode(' ',$this->scopes);
}
$response = $this->_post('/oauth/token',$parameters);
if (isset($response['html']["access_token"])) {
$this->token['access_token'] = $response['html']['access_token'];
$this->token['token_type'] = $response['html']['token_type'];
}
return $response;
}
public function get_access_token ($redirect_uri,$code) {
$parameters = array();
$parameters['grant_type'] = 'authorization_code';
$parameters['redirect_uri'] = $redirect_uri;
$parameters['client_id'] = $this->client_id;
$parameters['client_secret'] = $this->client_secret;
$parameters['code'] = $code;
$response = $this->_post('/oauth/token',$parameters);
if (isset($response['html']["access_token"])) {
$this->token['access_token'] = $response['html']['access_token'];
$this->token['token_type'] = $response['html']['token_type'];
}
return $response;
}
/**
* accounts
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
* int $response['id']
* string $response['username']
* string $response['acct']
* string $response['display_name'] The name to display in the user's profile
* bool $response['locked']
* string $response['created_at']
* int $response['followers_count']
* int $response['following_count']
* int $response['statuses_count']
* string $response['note'] A new biography for the user
* string $response['url']
* string $response['avatar'] A base64 encoded image to display as the user's avatar
* string $response['avatar_static']
* string $response['header'] A base64 encoded image to display as the user's header image
* string $response['header_static']
*/
public function accounts ($id) {
$response = $this->_get('/api/v1/accounts/'.$id);
return $response;
}
/**
* accounts_verify_credentials
*
* Getting the current user
*
* @return array $response
*/
public function accounts_verify_credentials () {
$response = $this->_get('/api/v1/accounts/verify_credentials');
return $response;
}
/**
* accounts_update_credentials
*
* Updating the current user
*
* @param array $parameters
* string $parameters['display_name'] The name to display in the user's profile
* string $parameters['note'] A new biography for the user
* string $parameters['avatar'] A base64 encoded image to display as the user's avatar
* string $parameters['header'] A base64 encoded image to display as the user's header image
*
* @return array $response
*/
public function accounts_update_credentials ($parameters) {
$response = $this->_patch('/api/v1/accounts/update_credentials',$parameters);
return $response;
}
/**
* accounts_followers
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_followers ($id) {
$response = $this->_get('/api/v1/accounts/'.$id.'/followers');
return $response;
}
/**
* accounts_following
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_following ($id) {
$response = $this->_get('/api/v1/accounts/'.$id.'/following');
return $response;
}
/**
* accounts_statuses
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_statuses ($id) {
$response = $this->_get('/api/v1/accounts/'.$id.'/statuses');
return $response;
}
/**
* accounts_follow
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_follow ($id) {
$response = $this->_post('/api/v1/accounts/'.$id.'/follow');
return $response;
}
/**
* accounts_unfollow
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_unfollow ($id) {
$response = $this->_post('/api/v1/accounts/'.$id.'/unfollow');
return $response;
}
/**
* accounts_block
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_block ($id) {
$response = $this->_post('/api/v1/accounts/'.$id.'/block');
return $response;
}
/**
* accounts_unblock
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_unblock ($id) {
$response = $this->_post('/api/v1/accounts/'.$id.'/unblock');
return $response;
}
/**
* accounts_mute
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_mute ($id) {
$response = $this->_post('/api/v1/accounts/'.$id.'/mute');
return $response;
}
/**
* accounts_unmute
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_unmute ($id) {
$response = $this->_post('/api/v1/accounts/'.$id.'/unmute');
return $response;
}
/**
* accounts_relationships
*
* @see https://your-domain/web/accounts/:id
*
* @param array $parameters
* int $parameters['id']
*
* @return array $response
* int $response['id']
* bool $response['following']
* bool $response['followed_by']
* bool $response['blocking']
* bool $response['muting']
* bool $response['requested']
*/
public function accounts_relationships ($parameters) {
$response = $this->_get('/api/v1/accounts/relationships',$parameters);
return $response;
}
/**
* accounts_search
*
* @param array $parameters
* string $parameters['q']
* int $parameters['limit'] default : 40
*
* @return array $response
*/
public function accounts_search ($parameters) {
$response = $this->_get('/api/v1/accounts/search',$parameters);
return $response;
}
/**
* blocks
*
* @return array $response
*/
public function blocks () {
$response = $this->_get('/api/v1/blocks');
return $response;
}
/**
* favourites
*
* @return array $response
*/
public function favourites () {
$response = $this->_get('/api/v1/favourites');
return $response;
}
/**
* follow_requests
*
* @return array $response
*/
public function follow_requests () {
$response = $this->_get('/api/v1/follow_requests');
return $response;
}
/**
* follow_requests_authorize
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function follow_requests_authorize ($id) {
$response = $this->_post('/api/v1/follow_requests/authorize',array('id'=>$id));
return $response;
}
/**
* follow_requests_reject
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @return array $response
*/
public function follow_requests_reject ($id) {
$response = $this->_post('/api/v1/follow_requests/reject',array('id'=>$id));
return $response;
}
/**
* follows
*
* Following a remote user
*
* @param string $uri username@domain of the person you want to follow
* @return array $response
*/
public function follows ($uri) {
$response = $this->_post('/api/v1/follows',array('uri'=>$uri));
return $response;
}
/**
* instance
*
* Getting instance information
*
* @return array $response
* string $response['uri']
* string $response['title']
* string $response['description']
* string $response['email']
*/
public function instance () {
$response = $this->_get('/api/v1/instance');
return $response;
}
/**
* media
*
* Uploading a media attachment
*
* @param string $file_path local path / http path
*
* @return array $response
* int $response['id'] ID of the attachment
* string $response['type'] One of: "image", "video", "gifv"
* string $response['url'] URL of the locally hosted version of the image
* string $response['remote_url'] For remote images, the remote URL of the original image
* string $response['preview_url'] URL of the preview image
* string $response['text_url'] Shorter URL for the image, for insertion into text (only present on local images)
*/
public function media ($file_path) {
$url = $this->mastodon_url.'/api/v1/media';
$parameters = $data = array();
$parameters[CURLOPT_HTTPHEADER] = array('Content-Type'=>'multipart/form-data');
$parameters[CURLOPT_POST] = true;
// set access_token
if (isset($this->token['access_token'])) {
$parameters[CURLOPT_POSTFIELDS]['access_token'] = $this->token['access_token'];
}
if (is_file($file_path)) {
$mime_type = mime_content_type($file_path);
$cf = curl_file_create($file_path,$mime_type,'file');
$parameters[CURLOPT_POSTFIELDS]['file'] = $cf;
}
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* mutes
*
* Fetching a user's mutes
*
* @return array $response
*/
public function mutes () {
$response = $this->_get('/api/v1/mutes');
return $response;
}
/**
* notifications
*
* @param int $id
*
* @return array $response
*/
public function notifications ($id = 0) {
$url = '/api/v1/notifications';
if ($id > 0) {
$url .= '/'.$id;
}
$response = $this->_get($url);
return $response;
}
/**
* notifications_clear
*
* Clearing notifications
*
* @return array $response
*/
public function notifications_clear () {
$response = $this->_post('/api/v1/notifications/clear');
return $response;
}
/**
* get_reports
*
* Fetching a user's reports
*
* @return array $response
*/
public function get_reports () {
$response = $this->_get('/api/v1/reports');
return $response;
}
/**
* post_reports
*
* Reporting a user
*
* @param array $parameters
* int $parameters['account_id'] The ID of the account to report
* int $parameters['status_ids'] The IDs of statuses to report (can be an array)
* string $parameters['comment'] A comment to associate with the report.
*
* @return array $response
*/
public function post_reports ($parameters) {
$response = $this->_post('/api/v1/reports',$parameters);
return $response;
}
/**
* search
*
* Searching for content
*
* @param array $parameters
* string $parameters['q'] The search query
* string $parameters['resolve'] Whether to resolve non-local accounts
*
* @return array $response
*/
public function search ($parameters) {
$response = $this->_get('/api/v1/search',$parameters);
return $response;
}
/**
* statuses
*
* Fetching a status
*
* @param int $id
*
* @return array $response
*/
public function statuses ($id) {
$response = $this->_get('/api/v1/statuses/'.$id);
return $response;
}
/**
* statuses_context
*
* Getting status context
*
* @param int $id
*
* @return array $response
*/
public function statuses_context ($id) {
$response = $this->_get('/api/v1/statuses/'.$id.'/context');
return $response;
}
/**
* statuses_card
*
* Getting a card associated with a status
*
* @param int $id
*
* @return array $response
*/
public function statuses_card ($id) {
$response = $this->_get('/api/v1/statuses/'.$id.'/card');
return $response;
}
/**
* statuses_reblogged_by
*
* Getting who reblogged a status
*
* @param int $id
*
* @return array $response
*/
public function statuses_reblogged_by ($id) {
$response = $this->_get('/api/v1/statuses/'.$id.'/reblogged_by');
return $response;
}
/**
* statuses_favourited_by
*
* Getting who favourited a status
*
* @param int $id
*
* @return array $response
*/
public function statuses_favourited_by ($id) {
$response = $this->_get('/api/v1/statuses/'.$id.'/favourited_by');
return $response;
}
/**
* post_statuses
*
* @param array $parameters
* string $parameters['status'] The text of the status
* int $parameters['in_reply_to_id'] (optional): local ID of the status you want to reply to
* int $parameters['media_ids'] (optional): array of media IDs to attach to the status (maximum 4)
* string $parameters['sensitive'] (optional): set this to mark the media of the status as NSFW
* string $parameters['spoiler_text'] (optional): text to be shown as a warning before the actual content
* string $parameters['visibility'] (optional): either "direct", "private", "unlisted" or "public"
*
* @return array $response
*/
public function post_statuses ($parameters) {
$response = $this->_post('/api/v1/statuses',$parameters);
return $response;
}
/**
* delete_statuses
*
* Deleting a status
*
* @param int $id
*
* @return array $response empty
*/
public function delete_statuses ($id) {
$response = $this->_delete('/api/v1/statuses/'.$id);
return $response;
}
/**
* statuses_reblog
*
* Reblogging a status
*
* @param int $id
*
* @return array $response
*/
public function statuses_reblog ($id) {
$response = $this->_post('/api/v1/statuses/'.$id.'/reblog');
return $response;
}
/**
* statuses_unreblog
*
* Unreblogging a status
*
* @param int $id
*
* @return array $response
*/
public function statuses_unreblog ($id) {
$response = $this->_post('/api/v1/statuses/'.$id.'/unreblog');
return $response;
}
/**
* statuses_favourite
*
* Favouriting a status
*
* @param int $id
*
* @return array $response
*/
public function statuses_favourite ($id) {
$response = $this->_post('/api/v1/statuses/'.$id.'/favourite');
return $response;
}
/**
* statuses_unfavourite
*
* Unfavouriting a status
*
* @param int $id
*
* @return array $response
*/
public function statuses_unfavourite ($id) {
$response = $this->_post('/api/v1/statuses/'.$id.'/unfavourite');
return $response;
}
/**
* timelines_home
*
* @return array $response
*/
public function timelines_home () {
$response = $this->_get('/api/v1/timelines/home');
return $response;
}
/**
* timelines_public
*
* @param array $parameters
* bool $parameters['local'] Only return statuses originating from this instance
*
* @return array $response
*/
public function timelines_public ($parameters = array()) {
$response = $this->_get('/api/v1/timelines/public',$parameters);
return $response;
}
/**
* timelines_tag
*
* @param string $hashtag
* @param array $parameters
* bool $parameters['local'] Only return statuses originating from this instance
*
* @return array $response
*/
public function timelines_tag ($hashtag,$parameters = array()) {
$response = $this->_get('/api/v1/timelines/tag/'.$hashtag,$parameters);
return $response;
}
}

View File

@ -0,0 +1,19 @@
# Mastodon-api-php
A GNU Social-compatible microblogging server https://mastodon.social PHP API
## How to use
require_once '/path/Mastodon_api.php';<br />
<br />
$mastodon_api = new Mastodon_api();<br />
$mastodon_api->set_url('Mastodon url');<br />
<br />
// print_r($mastodon_api->create_app('APP Name',null,null,'Mastodon url'));<br />
$mastodon_api->set_client('client_id','client_secret');<br />
<br />
// print_r($mastodon_api->login('your login email','your login password'));<br />
$mastodon_api->set_token('access_token','token_type');<br />
<br />
$mastodon_api->timelines_home();
## Test Mastodon
https://ery.kr

82
authorize/Mastodon.php Executable file
View File

@ -0,0 +1,82 @@
<?php
#!/usr/bin/env php
namespace HalcyonSuite\HalcyonForMastodon;
require_once('database.php');
require_once('Mastodon-api-php/Mastodon_api.php');
use HalcyonSuite\HalcyonForMastodon\Database;
use PDO;
use Exception;
/*-------------------
class for halcyon
--------------------*/
class Mastodon extends \Mastodon_api
{
function __construct(){
$appSettings = parse_ini_file('../../config.ini', true);
$this->clientName = $appSettings["App"]["api_client_name"];
$this->clientRedirectUris = $appSettings["App"]["api_client_website"].'/auth urn:ietf:wg:oauth:2.0:oob';
$this->clientWebsite = $appSettings["App"]["api_client_website"];
$this->clientScopes = array('read', 'write', 'follow');
$this->instances = array();
$this->dbHost = $appSettings["Mysql"]["db_host"];
$this->dbUser = $appSettings["Mysql"]["db_user"];
$this->dbPass = $appSettings["Mysql"]["db_pass"];
$this->dbName = $appSettings["Mysql"]["db_name"];
$this->database = new Database($this->dbHost, $this->dbUser, $this->dbPass, $this->dbName);
$this->readInstances();
}
/* note: $domainって書いてあるけど、ドメインじゃなくてURLです。すみません */
private function newInstance($domain)
{
$res = $this->create_app($this->clientName, $this->clientScopes, $this->clientRedirectUris, $this->clientWebsite);
if (isset($res['html']['client_id'])) {
$this->instances[$domain] = $res['html'];
$this->database->dbExecute("insert into instances(domain, client_id, client_secret) values(?,?,?)", array($domain, $res['html']['client_id'], $res['html']['client_secret']));
// insert into instances(domain, client_id, client_secret) values($domain, $client_id, $client_secret)
}else{
throw new Exception("Invalid instance");
}
}
public function selectInstance($domain)
{
$this->set_url($domain);
if (!$this->instanceExists($domain)) {
$this->newInstance($domain);
}
$this->set_client($this->instances[$domain]['client_id'], $this->instances[$domain]['client_secret']);
}
public function getInstance($domain)
{
$this->set_url($domain);
if (!$this->instanceExists($domain)) {
$this->newInstance($domain);
}
return array('client_id' => $this->instances[$domain]['client_id'], 'client_secret' => $this->instances[$domain]['client_secret']);
}
public function instanceExists($domain)
{
return isset($this->instances[$domain]);
}
private function readInstances()
{
$stmt = $this->database->dbExecute("select domain,client_id,client_secret from instances");
foreach($stmt->fetchAll(PDO::FETCH_ASSOC) as $row){
$this->instances[$row['domain']] = $row;
}
}
}
?>

50
authorize/database.php Executable file
View File

@ -0,0 +1,50 @@
<?php
#!/usr/bin/env php
namespace HalcyonSuite\HalcyonForMastodon;
use PDO;
use Exception;
class Database{
public function __construct($dbhost, $dbuser, $dbpass, $dbname){
$this->dbhost = $dbhost;
$this->dbuser = $dbuser;
$this->dbpass = $dbpass;
$this->dbname = $dbname;
$this->dsn = "mysql:dbname=".$this->dbname.";host=".$this->dbhost.";charset=utf8";
$this->connecting = false;
$this->dbConnect();
}
public function dbConnect($commit=True){
try{
$dbh = new PDO($this->dsn, $this->dbuser, $this->dbpass);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
if(!$commit){
$dbh->beginTransaction();
}
}catch (PDOException $e){
throw new Exception($e);
}
$this->dbh = $dbh;
$this->connecting = true;
return $dbh;
}
public function dbClose(){
$this->dbh = Null;
$this->connecting = false;
}
public function dbExecute($sql, $attr = null){
if ($attr === null) {
$attr = array();
}
if (!$this->connecting) {
$this->dbConnect();
}
$stmt = $this->dbh->prepare($sql);
$stmt->execute($attr);
return $stmt;
}
}
?>

11
config.ini.sample Normal file
View File

@ -0,0 +1,11 @@
; Registar App Settings
[App]
api_client_name = APPLICATION'S NAME
api_client_website = HTTPS://YOURDOMAIN.COM/
; MySQL Settings
[Mysql]
db_host = YOUR DATABASE'S HOST
db_user = YOUR DATABASE'S USER
db_pass = YOUR DATABASE'S PASSWORD
db_name = YOUR DATABASE'S NAME

56
public_html/.htaccess Normal file
View File

@ -0,0 +1,56 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+?)$
RewriteRule ^(.*)$ https://$1 [R=301,L]
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Main
RewriteBase /
RewriteRule ^home/?$ / [R=301]
RewriteBase /login
RewriteRule ^login/?$ login/login\.php [NC,L,QSA]
RewriteRule ^auth/?$ login/auth\.php [NC,L,QSA]
RewriteRule ^logout/?$ login/logout\.php [NC,L,QSA]
RewriteRule ^terms/?$ login/terms\.php [NC,L,QSA]
# LTL
RewriteBase /local
RewriteRule ^local/?$ local\.php [NC,L]
# FTL
RewriteBase /federated
RewriteRule ^federated/?$ federated\.php [NC,L]
# Notice
RewriteBase /notifications
RewriteRule ^notifications/?$ notifications\.php [NC,L]
# Search
RewriteBase /search
RewriteRule ^search/?$ search_hash_tag\.php [NC,L,QSA]
RewriteRule ^search/users/?$ search_user\.php [NC,L,QSA]
# User
RewriteBase /
RewriteRule ^@(.+)@(.+)\.([a-z]+)/?$ user\.php?user=@$1@$2\.$3 [NC,L,QSA]
RewriteRule ^@(.+)@(.+)\.([a-z]+)/status/(.+?)?$ user\.php?user=@$1@$2\.$3&status=$4 [NC,L,QSA]
RewriteRule ^@(.+)@(.+)\.([a-z]+)/media/?$ user_only_media\.php?user=@$1@$2\.$3 [NC,L,QSA]
RewriteRule ^@(.+)@(.+)\.([a-z]+)/with_replies/?$ user_include_replies\.php?user=@$1@$2\.$3 [NC,L,QSA]
RewriteRule ^@(.+)@(.+)\.([a-z]+)/followers/?$ user_followers\.php?user=@$1@$2\.$3 [NC,L,QSA]
RewriteRule ^@(.+)@(.+)\.([a-z]+)/following/?$ user_following\.php?user=@$1@$2\.$3 [NC,L,QSA]
RewriteRule ^@(.+)@(.+)\.([a-z]+)/favourites/?$ user_favorite\.php?user=@$1@$2\.$3 [NC,L,QSA]
# Image
RewriteBase /
RewriteRule ^avatars/original/missing\.png$ assets/images/missing\.png [NC,L]
RewriteRule ^headers/original/missing\.png$ assets/images/missing_header\.png [NC,L]
# 404
RewriteRule ^404/?$ 404\.php [NC,L,QSA]
ErrorDocument 404 /404
</IfModule>

25
public_html/404.php Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Halcyon / ?</title>
<link rel="shortcut icon" href="/assets/images/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/assets/css/404.css" media="all" />
</head>
<body>
<article>
<h1>404</h1>
<h2>Sorry, something went wrong.</h2>
</article>
</body>
</html>

View File

@ -0,0 +1,169 @@
@charset "utf-8";
/*--------------------------------------
Reset
--------------------------------------*/
* {
margin: 0;
padding: 0;
font-size: 100%;
}
a {
text-decoration: none;
word-break: break-all;
color: inherit;
}
a:hover {
text-decoration: underline;
}
ul, ol {
list-style: none;
padding: 0;
margin: 0;
}
img {
vertical-align: top;
border: 0;
max-width: 100%;
max-height: 100%;
}
button {
font-size: 100%;
}
.clear {
clear: both;
}
.red {
color: red!important;
}
.invisible {
display: none!important;
}
.no-events {
pointer-events: none;
}
.disallow_select {
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
.text_ellipsis {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
h1, h2, h3, h4, h5, h6 {
font-weight: normal;
line-height: 1;
margin: 0;
padding: 0;
}
blockquote, q {
quotes: none;
margin: 0;
}
blockquote * {
margin: 0;
word-break: break-all;
}
blockquote:before, blockquote:after,
q:before, q:after {
content:'';
content:none;
}
article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
display:block;
}
textarea {
width: 100%;
}
input {
max-width: 100%;
}
button, input[type="submit"], input[type="button"]{
background-color: transparent;
border: none;
cursor: pointer;
outline: none;
padding: 0;
appearance: none;
}
/* Twitter Emoji Prefix */
img.emoji {
height: 1em;
width: 1em;
margin: 0 .05em 0 .1em;
vertical-align: -0.1em;
}
.emoji_poss .auth_emoji {
display: inline-block;
height: 100%;
margin: auto;
margin-left: 4px;
}
/*--------------------------------------
Base
--------------------------------------*/
html {
font-family : "Helvetica Neue",Helvetica,"ヒラギノ角ゴ Pro W3",
"Hiragino Kaku Gothic Pro",Meiryo,"メイリオ"," Pゴシック",Arial,sans-serif;
font-size : 100%;
line-height : 1;
color: #333;
min-width: 100%;
min-height: 100%;
}
body {
margin: 0;
padding: 0;
min-width: 100%;
min-height: 100%;
word-wrap:break-word;
background-color: #189EFC;
}
article {
margin: auto;
padding: 24px;
}
h1 {
font-size: 248px;
text-align: center;
color: #fff;
font-weight: 600;
}
h2 {
font-size: 56px;
text-align: center;
color: #fff;
font-weight: 200;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B