빠른 시작
https://developers.facebook.com/docs/messenger-platform/guides/quick-start

Webhook 참조
https://developers.facebook.com/docs/messenger-platform/webhook-reference

가이드
https://developers.facebook.com/docs/messenger-platform/guides
Messenger 플랫폼의 핵심 영역에 대해 알아보세요.
  • 빠른 시작 - 10분 안에 실행하는 봇 만들기
  • 설정 - Messenger용 Facebook 앱 및 페이지 구성
  • 대화 - API 및 Webhooks를 사용하여 메시지 주고 받기
  • 진입점 - 사람들이 어떻게 봇과 대화를 시작할 수 있는지 알아보기
  • 결제(베타) - 봇에서 결제를 처리할 수 있음
  • m.me 링크 - m.me 링크에서 리퍼럴을 사용하는 방법 알아보기
  • 계정 연결 - 안전한 방식을 사용하여 웹에서 Messenger 사용자 ID 얻기
  • 분석 - 지표를 활용하여 봇의 성과 확인



Messenger Platform samples for sending and receiving messages. Walk through the Get Started with this code.
https://developers.facebook.com/docs/messenger-platform/quickstart

Posted by incree

2016/11/22 05:03 2016/11/22 05:03
, , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/373

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다


GCM 이용해서 Push Notifications 개발시 아래와 같은 에러가 나는 경우


2016-11-21 13:37:03.136446 ccourt[2223:955954] You've implemented -[<UIApplicationDelegate> application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.


Project -> Targets -> Capabilities  에서


Push Notifications 와 Background Modes.Remote notifications 를 ON으로 설정하면 된다


사용자 삽입 이미지

Posted by incree

2016/11/21 14:05 2016/11/21 14:05
, ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/372

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

[Android] Firebase 로 GCM 구현하기

구글 GCM이 Firebase로 바뀌면서 나중에 참고하기 위해서 정리한다

구글 계정이 있고, Android 프로젝트용

https://firebase.google.com/console/

사용자 삽입 이미지

구글계정이 있고 구글 계정과 연결해서 사용할때 firebase console에서 하단의 '여기를 클릭'을 클릭

사용자 삽입 이미지

Sign in with Google 클릭

사용자 삽입 이미지

허용 클릭

사용자 삽입 이미지

구글 계정으로 연동이 됐으면 다시 firebase console에서 '새 프로젝트 만들기'를 클릭한다

사용자 삽입 이미지

프로젝트 이름, 국가/지역을 선택하고 프로젝트 만들기를 클릭한다

사용자 삽입 이미지

프로젝트가 만들어 지면 프로젝트 화면으로 이동하는데, Notifications를 클릭하면 위 화면이 나온다. 운선 안드로이드를 클릭한다.

사용자 삽입 이미지

패키지 이름을 넣고 앱 추가를 클릭한다. - 중간에 설명처럼 Android Studio 2.2. 이상에서도 가능하다.

사용자 삽입 이미지

이전 단계에서 '앱 추가'를 클릭하면 자동으로 google-services.json이 다운로드 된다. 이 파일을 프로젝트에 추가한다.

사용자 삽입 이미지

gradle 파일을 수정하고 동기화 한 후 완료를 클릭한다
주의 : 2번 앱수순 gradle 파일 수정시 화살표의 'Add to the bottom of the file'를 반드시 지켜야 한다.

사용자 삽입 이미지

완료시 나오는 화면 앱 개발까지 완료되면 '첫 번째 메시지 보내기'를 클릭해서 테스트 하면 된다.

사용자 삽입 이미지

메시지를 발송해 본다



Posted by incree

2016/11/19 02:14 2016/11/19 02:14
, ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/371

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

페이스북용 챗봇 개발중에 php로 응답 보내는 부분 구현에 어려움을 겪어 정리 차원으로 올림

페이스북의 node.js 샘플 처럼 전체 파라미터를 json_encode해서 보내면 아래와 같은 에러가 리턴된다

{
  "error": {
    "message": "(#100) The parameter recipient is required",
    "type": "OAuthException",
    "code": 100,
    "fbtrace_id": "CdhFkKgAL19"
  }
}또는 형태에 따라

{
  "error": {
    "message": "(#100) param recipient must be non-empty.",
    "type": "OAuthException",
    "code": 100,
    "fbtrace_id": "BDoRMk2PNZU"
  }
}



아래 주석 부분 주의해서 다음과 같이 하면 정상 송신이 가능하다.
$messageData = array('recipient' => array('id' => $recipientId)
              , 'message' => array('text' => $messageText, 'metadata' => 'DEVELOPER_DEFINED_METADATA'));

$params = array(
        'recipient' => json_encode($messageData['recipient']),
        'message' => json_encode($messageData['message'])
    );

    $curl = curl_init('https://graph.facebook.com/v2.6/me/messages?access_token=your_token');
    curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);  // 이게 없으면 통신 자체가 안된다(에러 60)
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));  // 전체 json이 아니라 파라미터의 값만 json이어야 한다. node.js 예제에서는 content-type도 application/json이고 파라미터도 전체 json 데이타 이다. 그 코드가 정상 작동하는지 모르겠지만 php는 위와 같이 해야 정상작동

    $json_response = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

    curl_close($curl);

    $response = json_decode($json_response, true);




한글 메시지 전송을 위해서는 파일을 utf-8로 저장해야 한다  

Posted by incree

2016/11/11 21:53 2016/11/11 21:53
, , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/370

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

구글 클라우드 프린트를 이용해서 웹에 있는 파일(pdf) 인쇄하기

 전체 소스 : GoogleCloudPrintExample.zip




Android Integration
https://developers.google.com/cloud-print/docs/android

Posted by incree

2016/09/17 22:34 2016/09/17 22:34
,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/369

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

Custom ListView로 AlertDialog 띄우기


사용자 삽입 이미지

사용자 삽입 이미지

사용자 삽입 이미지


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.incree.customalertdialog">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.NoTitleBar">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>

</manifest>


MainActivity.java
package com.incree.customalertdialog;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void btnOpenPopupClickHandler(View view) {
final List<Product> items = new ArrayList<Product>();
items.add(new Product(1, "와퍼세트", "와퍼 단품+프렌치프라이R+콜라R", 8000, R.drawable.product_1));
items.add(new Product(2, "너겟킹10조각", "담백한 닭가슴살로 만든 바삭한 너켓킹", 5100, R.drawable.product_2));
items.add(new Product(3, "머쉬룸스테이크버거세트", "머쉬룸스테이크버거 단품+프렌치프라이R+콜라R", 9600, R.drawable.product_3));
items.add(new Product(4, "트리오팩", "통새우와퍼+와퍼+와퍼주니어+프렌치프라이(R)+치즈프라이+너겟킹4조각+어니언링+콜라(R)3", 24500, R.drawable.product_4));
items.add(new Product(5, "롱킹", "쇠고기 (호주산과 뉴질랜드산 섞음)", 6100, R.drawable.product_5));
final ProductListAdapter adapter = new ProductListAdapter(MainActivity.this, items);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
.setAdapter(adapter, null)
.setTitle("상품선택")
.setPositiveButton("선택구매", new DialogInterface.OnClickListener() { // 버튼은 테마에 따라서 모양이 다르게 모임
public void onClick(DialogInterface dialog, int whichButton) {
for(Product product : items) {
if (product.isSelected()) {
Log.d("MainActivity", product.getProductTitle() + " 선택");
}
}
}
})
.setNegativeButton("취소", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Log.d("MainActivity", "취소 터치");
}
});

AlertDialog alertDialog = builder.create();
final ListView listView = alertDialog.getListView();
listView.setAdapter(adapter);
//listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // 여러 상품 선택을 위해 - 이 형태에서는 필요 없다
listView.setDivider(new ColorDrawable(Color.LTGRAY));
listView.setDividerHeight(1);
listView.setFocusable(false); // false를 해줘야 row touch event 가능
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
CheckBox cbGoLargeChecked = (CheckBox)view.findViewById(R.id.productChecked);
Product tmpVo = items.get(position);
tmpVo.setSelected(!tmpVo.isSelected());
cbGoLargeChecked.setChecked(tmpVo.isSelected());
}
});

alertDialog.show();
}
}


class ProductListAdapter extends BaseAdapter
{
private Activity activity;
private List<Product> dataList;

public ProductListAdapter(Activity activity, List<Product> dataList) {
this.activity = activity;
this.dataList = dataList;
}

@Override
public int getCount()
{
return dataList.size();
}

@Override
public Object getItem(int position)
{
return dataList.get(position);
}

@Override
public long getItemId(int position)
{
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if(view == null) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.product_dialog_list_row, null);
}

final Product data = dataList.get(position);

ImageView ivProductImage = (ImageView)view.findViewById(R.id.productImage);
final CheckBox cbProductChecked = (CheckBox)view.findViewById(R.id.productChecked);
TextView tvProductTitle = (TextView)view.findViewById(R.id.productTitle);
TextView tvProductDesc = (TextView)view.findViewById(R.id.productDesc);

cbProductChecked.setFocusable(false); // false를 해줘야 row touch event 가능

cbProductChecked.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
data.setSelected(cbProductChecked.isChecked());
}
});

ivProductImage.setImageDrawable(activity.getResources().getDrawable(data.getProductImageResource()));
tvProductTitle.setText(data.getProductTitle());
tvProductDesc.setText(data.getProductDesc());
cbProductChecked.setText(data.getProductPrice() + "원");
cbProductChecked.setChecked(data.isSelected());

return view;
}
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.incree.customalertdialog.MainActivity">

<Button
android:id="@+id/btnOpenPopup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="btnOpenPopupClickHandler"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="상품 팝업 열기"/>
</RelativeLayout>





product_dialog_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/productImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_centerVertical="true"/>

<TextView
android:id="@+id/productTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginTop="5dp"
android:layout_toRightOf="@id/productImage"
android:layout_alignParentTop="true"
android:hint="상품명"
android:textColor="#252525"
android:textSize="16sp"
android:textStyle="bold"/>

<CheckBox
android:id="@+id/productChecked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layoutDirection="rtl"
android:textColor="#252525"
/>

<TextView
android:id="@+id/productDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_toRightOf="@id/productImage"
android:layout_toLeftOf="@id/productChecked"
android:layout_below="@id/productTitle"
android:hint="상품 설명"
android:textColor="#252525"
android:textSize="12sp"/>
</RelativeLayout>

 전체 소스 : CustomAlertDialog.zip


Posted by incree

2016/09/10 09:08 2016/09/10 09:08
, ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/368

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

안드로이드 웹뷰 회전시 상태 유지

android:configChanges="orientation|keyboardHidden|screenSize"

screenSize 는 Android 3.2(API level 13) or higher device


Posted by incree

2016/06/01 22:16 2016/06/01 22:16
,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/367

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

Swift Cookie sharing between multiple WKWebViews

https://github.com/ShingoFukuyama/WKWebViewTips

self.processPool = [[WKProcessPool alloc] init];

WKWebViewConfiguration *configuration1 = [[WKWebViewConfiguration alloc] init];
configuration1.processPool = self.processPool;
WKWebView *webView1 = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration1];
...
WKWebViewConfiguration *configuration2 = [[WKWebViewConfiguration alloc] init];
configuration2.processPool = self.processPool;
WKWebView *webView2 = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration2];

Posted by incree

2016/04/21 20:54 2016/04/21 20:54
, , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/366

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다




http://www.facebook.com/dialog/pagetab?app_id=YOUR_APP_ID&next=http://facebook.com

문서화되지 않은 방법: 또는 다음 URL을 호출하여도 탭 추가가 가능하다.

https://www.facebook.com/add.php?api_key=YOUR_APP_ID&pages

Updated(2012/03/16) 최근 페이스북이 앱페이지(앱 커뮤니티 페이지)의 관리자 메뉴 속에 앱을 페이지에 추가하기 메뉴를 추가했다. 앱페이지를 만들었다면 이 메뉴 기능을 이용해도 된다. 자세한 내용


Add Page Tab Dialog

https://developers.facebook.com/docs/pages/page-tab-dialog
FB.ui({
  method: 'pagetab',
  redirect_uri: 'YOUR_URL'
}, function(response){});
https://www.facebook.com/dialog/pagetab?
  app_id=YOUR_APP_ID
  &redirect_uri=YOUR_URL

 https://developers.facebook.com/docs/pages/tabs

FB.ui({
  method: 'pagetab',
  redirect_uri: 'YOUR_URL'
}, function(response){});
https://www.facebook.com/dialog/pagetab?
  app_id=YOUR_APP_ID
  &redirect_uri=YOUR_URL

Posted by incree

2015/08/21 23:52 2015/08/21 23:52
, , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/365

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

[MySQL] MySQL server has gone away error


몇일전 운영하는 사이트에서 이 에러가 발생해서 정리

MySQL 공식사이트에서 이 에러에 대한 페이지를 보면(https://dev.mysql.com/doc/refman/5.0/en/gone-away.html)
이 에러는 증상에 따라 에러 코드가 두가지로 나뉜다.

2006 : CR_SERVER_GONE_ERROR : The client couldn't send a question to the server.
2013 : CR_SERVER_LOST : The client didn't get an error when writing to the server, but it didn't get a full answer (or any answer) to the question.


원인을 크게 세가지로 나눌수 있는데

1. 어떤 이유에서든 MySQL 서버와 연결이 끊어진 경우(wait_timeout 등)
 - 재연결하거나 끊어진 원인을 파악해서 끊어지지 않게 처리

2. 쿼리가 너무 큰 경우( max_allowed_packet variable 에 설정된 값보다 큰 쿼리를 실행하면 발생)
 - variable 값을 키우거나 쿼리를 작게 만든다

3. --skip-networking 옵션을 사용하여 MySQL을 시작한 경우
 - 이 옵션을 빼고 시작한다.


Posted by incree

2015/07/29 12:30 2015/07/29 12:30
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/364

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

« Previous : 1 : 2 : 3 : 4 : 5 : 6 : ... 14 : Next »

Notices

Archives

Authors

  1. incree

Recent Trackbacks

Calendar

«   2017/12   »
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

Site Stats

Total hits:
436147
Today:
3
Yesterday:
155