001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.fukurou.util;
017
018import org.opengion.fukurou.system.OgRuntimeException ;
019
020import java.io.OutputStream;
021import java.io.BufferedWriter;
022import java.io.OutputStreamWriter;
023import java.net.HttpURLConnection;
024import java.net.URL;
025import java.net.URI;                                                                                                                    // 8.5.3.2 (2023/10/13)
026import java.net.Proxy;
027import java.net.InetSocketAddress;
028import java.net.URLEncoder;
029
030/**
031 * LineNotify は、Line Notify を利用してLineメッセージを送信します。
032 *
033 * 《手順》
034 *  1.https://notify-bot.line.me/ja/ からログインして
035 *    マイページから「アクセストークンを発行」をクリック
036 *  2.トークンを設定する。
037 *
038 * <pre>
039 * Usage: java org.opengion.fukurou.util.LineNotify -token アクセストークン -message メッセージ -stickerPackageId 4 -stickerId 614
040 * </pre>
041 *
042 * 《SystemのProxy設定を使用》
043 * java 起動時に、-Djava.net.useSystemProxies=true を指定する。
044 * プログラム内で設定済みです。
045 *
046 * @og.rev 7.2.6.2 (2020/07/22) 新規作成
047 *
048 * @version  7.2
049 * @author   Kazuhiko Hasegawa
050 * @since    JDK11.0,
051 */
052// 8.5.5.1 (2024/02/29) spotbugs CT_CONSTRUCTOR_THROW(コンストラクタで、Excweptionを出さない) class を final にすれば、警告は消える。
053// public class LineNotify {
054public final class LineNotify {
055        private final String token;
056        private final Proxy  proxy;
057
058        private String  stmpPkg ;
059        private String  stmpId ;
060
061        /**
062         * アクセストークンを指定した、コンストラクター
063         *
064         * Proxyは、java.net.useSystemProxies=true 設定を使用します。
065         *
066         * @param       token   アクセストークン
067         */
068        public LineNotify( final String token ) {
069                this.token = token;
070                this.proxy = null;
071
072                System.setProperty("java.net.useSystemProxies","true");         // Proxyの自動設定
073        }
074
075        /**
076         * アクセストークンを指定した、コンストラクター
077         *
078         * Proxyが、nullか、ゼロ文字列の場合は、Proxy.NO_PROXY を指定します。
079         * そうでない場合は、アドレス:ポート形式で指定します。
080         *
081         *
082         * @param       token   アクセストークン(Line Notifyで取得したID)
083         * @param       proxy   アドレス:ポート形式で指定します。
084         */
085        public LineNotify( final String token,final String proxy ) {
086                this.token = token;
087                if( proxy == null || proxy.isEmpty() ) {
088                        this.proxy = Proxy.NO_PROXY;
089                }
090                else {
091                        final int ad = proxy.indexOf( ':' );
092                        final String adrs = proxy.substring( 0,ad );
093                        final int    port = Integer.parseInt( proxy.substring( ad+1 ) );
094
095                        this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(adrs, port));
096                }
097        }
098
099        /**
100         * スタンプ(スティッカー)を指定します。
101         *
102         * 指定は、パッケージIDとIDの両方とも必要です。
103         *
104         * @param       stmpPkg stickerPackageIdを指定します。
105         * @param       stmpId  stickerIdを指定します。
106         */
107        public void setStamp( final String stmpPkg , final String stmpId ) {
108                this.stmpPkg = stmpPkg;
109                this.stmpId  = stmpId ;
110        }
111
112        /**
113         * メッセージを送信します。
114         *
115         * 送信は、コネクションを毎回disconnectします。
116         *
117         * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。 警告: [deprecation] URLのURL(String)は推奨されません
118         * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応
119         *
120         * @param       message 送信するメッセージ
121         */
122        public void notify( final String message ) {
123                // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応
124                String errMsg = null;
125
126                HttpURLConnection connection = null;
127                try {
128//                      final URL url = new URL( "https://notify-api.line.me/api/notify" );
129                        // new URI() で作ると、URISyntaxException が発生するが、URI.createで作ると、IllegalArgumentException にラッパーされる。
130                        final URL url = URI.create( "https://notify-api.line.me/api/notify" ).toURL();          // 8.5.3.2 (2023/10/13)
131                        if( proxy == null ) {
132                                connection = (HttpURLConnection) url.openConnection();
133                        }
134                        else {
135                                connection = (HttpURLConnection) url.openConnection(proxy);
136                        }
137                        connection.setDoOutput( true );                         // POST可能にする
138                        connection.setDoInput( true );                          // getResponseCode を取得するため
139                        connection.setRequestMethod( "POST" );
140                        connection.addRequestProperty( "Authorization", "Bearer " + token );
141                        connection.setConnectTimeout( 10*1000 );        // 10秒でタイムアウト
142
143                        try( OutputStream os = connection.getOutputStream();
144                                 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")) ) {
145
146//                              writer.append( "message=" ).append( message );
147                                writer.append( "message=" ).append( URLEncoder.encode(message, "UTF-8") );
148                                if( !StringUtil.isNull( stmpPkg,stmpId ) ) {
149                                        writer.append( "&stickerPackageId=" ).append( stmpPkg );
150                                        writer.append( "&stickerId="        ).append( stmpId  );
151                                }
152                                writer.flush();
153
154                                final int status = connection.getResponseCode();
155                                if( status != HttpURLConnection.HTTP_OK ) {
156                                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応
157//                                      final String errMsg = HttpConnect.code2Message( status );
158//                                      throw new OgRuntimeException( errMsg );
159                                        errMsg = HttpConnect.code2Message( status );
160                                }
161                        }
162                }
163                catch( final Throwable th ) {
164                        throw new OgRuntimeException( th );
165                }
166                finally {
167                        if( connection != null ) {
168                                connection.disconnect();
169                        }
170                }
171
172                // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応
173                if( errMsg != null ) {
174                        throw new OgRuntimeException( errMsg );
175                }
176        }
177
178        /**
179         * サンプル実行用のメインメソッド。
180         *
181         * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidReassigningLoopVariables
182         *
183         * @param       args    コマンド引数配列。
184         */
185        public static void main( final String[] args ) {
186                String token   = "XXXX";
187                String message = "テスト";
188                String stmpPkg = "4";
189                String stmpId  = "614";
190
191                // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidReassigningLoopVariables
192//              for( int i=0; i<args.length; i++ ) {
193//                      if( "-token".equalsIgnoreCase( args[i] ) ) {
194//                              token = args[++i];
195//                      }
196//                      else if( "-message".equalsIgnoreCase( args[i] ) ) {
197//                              message = args[++i];
198//                      }
199//                      else if( "-stickerPackageId".equalsIgnoreCase( args[i] ) ) {
200//                              stmpPkg = args[++i];
201//                      }
202//                      else if( "-stickerId".equalsIgnoreCase( args[i] ) ) {
203//                              stmpId = args[++i];
204//                      }
205//              }
206                final int len = args.length;
207                int idx = 0;
208                while( idx < len ) {
209                        final String key = args[idx++];
210                        if( "-token".equalsIgnoreCase( key ) ) {
211                                token = args[idx];
212                        }
213                        else if( "-message".equalsIgnoreCase( key ) ) {
214                                message = args[idx];
215                        }
216                        else if( "-stickerPackageId".equalsIgnoreCase( key ) ) {
217                                stmpPkg = args[idx];
218                        }
219                        else if( "-stickerId".equalsIgnoreCase( key ) ) {
220                                stmpId = args[idx];
221                        }
222                }
223
224                final LineNotify lineNotify = new LineNotify( token );
225                lineNotify.setStamp( stmpPkg,stmpId );
226                lineNotify.notify( message );
227        }
228}